diff --git a/mkp/fritzbox_smarthome-0.8.3-20231230.mkp b/mkp/fritzbox_smarthome-0.8.3-20231230.mkp
index 3861f7e52d2a579d8db61d0d1c8e5f2b03cd398c..fc445537db31dbe328abb9dbd0a7a0155cd678ae 100644
Binary files a/mkp/fritzbox_smarthome-0.8.3-20231230.mkp and b/mkp/fritzbox_smarthome-0.8.3-20231230.mkp differ
diff --git a/source/agent_based/fritzbox_smarthome.py b/source/agent_based/fritzbox_smarthome.py
new file mode 100644
index 0000000000000000000000000000000000000000..95a42b578dee78829556392410c088541e4f070e
--- /dev/null
+++ b/source/agent_based/fritzbox_smarthome.py
@@ -0,0 +1,147 @@
+#!/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 typing import Dict
+
+from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+    Result,
+    Service,
+    State,
+    register,
+    HostLabel,
+)
+from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
+    CheckResult,
+    DiscoveryResult,
+    HostLabelGenerator,
+    StringTable,
+)
+
+from cmk.base.plugins.agent_based.utils.fritzbox_smarthome import (
+    AvmSmartHomeDevice,
+    parse_avm_smarthome_device,
+)
+
+
+def host_label_fritzbox_smarthome(section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]) -> HostLabelGenerator:
+    yield HostLabel(name="fritz/smarthome/device", value="yes")
+    if isinstance(section, AvmSmartHomeDevice):  # piggyback
+        yield HostLabel(
+            name="fritz/smarthome/device_type",
+            value=section.product_name.replace(' ', '_').lower()
+        )
+
+
+def parse_fritzbox_smarthome(string_table: StringTable) -> AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice] | None:
+    try:
+        raw_devices = json.loads(str(string_table[0][0]))
+    except json.JSONDecodeError:
+        return
+
+    if isinstance(raw_devices, Dict):
+        return parse_avm_smarthome_device(raw_devices)
+    else:
+        return {raw_device['id']: parse_avm_smarthome_device(raw_device) for raw_device in raw_devices}
+
+
+register.agent_section(
+    name="fritzbox_smarthome",
+    parse_function=parse_fritzbox_smarthome,
+    host_label_function=host_label_fritzbox_smarthome,
+)
+
+
+def discovery_fritzbox_smarthome_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        yield Service()
+
+
+def discovery_fritzbox_smarthome_multiple(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        for device_id, device in section.items():
+            yield Service(item=str(device_id))
+
+
+def check_fritzbox_smarthome_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
+
+    _tx_busy = {
+        0: 'no',
+        1: 'yes',
+    }
+
+    yield Result(
+        state=State.OK,
+        notice=f'Device: {section.manufacturer} {section.product_name}, FW: {section.fw_version}'
+    )
+
+    if section.present == 0:
+        yield Result(state=State(params['present']), summary='Device is offline')
+        # stop if device is not present
+        return
+
+    yield Result(state=State.OK, summary='Device is online')
+
+    if section.tx_busy is not None:
+        yield Result(
+            state=State.OK,
+            notice=f'Sending command: {_tx_busy.get(section.tx_busy, f"unknown ({section.tx_busy})")}'
+        )
+
+
+def check_fritzbox_smarthome_multiple(
+        item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if isinstance(section, Dict):
+        try:
+            device = section[item]
+        except KeyError:
+            return
+
+        yield from check_fritzbox_smarthome_single(params, device)
+        yield Result(state=State.OK, summary=f'[{device.name}]')
+
+
+_fritzbox_smarthome_parameters = {
+    'present': 1,
+}
+
+register.check_plugin(
+    name="fritzbox_smarthome_single",
+    service_name="Device status",
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_single,
+    check_function=check_fritzbox_smarthome_single,
+    check_ruleset_name="fritzbox_smarthome_single",
+    check_default_parameters=_fritzbox_smarthome_parameters,
+)
+
+
+register.check_plugin(
+    name="fritzbox_smarthome_multiple",
+    service_name="Smarthome Device status %s",
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_multiple,
+    check_function=check_fritzbox_smarthome_multiple,
+    check_ruleset_name="fritzbox_smarthome_multiple",
+    check_default_parameters=_fritzbox_smarthome_parameters,
+)
diff --git a/source/agent_based/fritzbox_smarthome_app_lock.py b/source/agent_based/fritzbox_smarthome_app_lock.py
new file mode 100644
index 0000000000000000000000000000000000000000..b52314e868c9feb5c542d517f7c46be96c46b179
--- /dev/null
+++ b/source/agent_based/fritzbox_smarthome_app_lock.py
@@ -0,0 +1,89 @@
+#!/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_app_lock.py (check plugin)
+#
+#
+
+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
+
+
+def discovery_fritzbox_smarthome_app_lock_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        if section.lock is not None:
+            yield Service()
+
+
+def discovery_fritzbox_smarthome_app_lock_multiple(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        for device_id, device in section.items():
+            if device.lock is not None:
+                yield Service(item=str(device_id))
+
+
+def check_fritzbox_smarthome_app_lock_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
+    if section.lock is None:
+        return
+
+    def _get_status(status: int):
+        _lock = {
+            0: 'is not deactivated',
+            1: 'is deactivated',
+        }
+        return _lock.get(status, f'unknown ({status})')
+
+    yield Result(state=State.OK, summary=f'Manual access for phone, app or user interface {_get_status(section.lock)}')
+
+
+def check_fritzbox_smarthome_app_lock_multiple(
+        item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if isinstance(section, Dict):
+        try:
+            yield from check_fritzbox_smarthome_app_lock_single(params, section[item])
+        except KeyError:
+            return
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_app_lock_single',
+    service_name='APP lock',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_app_lock_single,
+    check_function=check_fritzbox_smarthome_app_lock_single,
+    # check_ruleset_name='fritzbox_smarthome_app_lock',
+    check_default_parameters={}
+)
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_app_lock_multiple',
+    service_name='Smarthome APP lock %s',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_app_lock_multiple,
+    check_function=check_fritzbox_smarthome_app_lock_multiple,
+    # check_ruleset_name='fritzbox_smarthome_app_lock',
+    check_default_parameters={}
+)
diff --git a/source/agent_based/fritzbox_smarthome_battery.py b/source/agent_based/fritzbox_smarthome_battery.py
new file mode 100644
index 0000000000000000000000000000000000000000..61d597b61e08a8b772b93bc69a952ca0ef4cd051
--- /dev/null
+++ b/source/agent_based/fritzbox_smarthome_battery.py
@@ -0,0 +1,92 @@
+#!/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-29
+# File  : fritzbox_smarthome_battery.py (check plugin)
+#
+#
+
+from typing import Dict
+
+from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+    Result,
+    Service,
+    State,
+    register,
+)
+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
+
+
+def discovery_fritzbox_smarthome_battery_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        if section.battery_low is not None:
+            yield Service()
+
+
+def discovery_fritzbox_smarthome_battery_multiple(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        for device_id, device in section.items():
+            if device.battery_low is not None:
+                yield Service(item=str(device_id))
+
+
+def check_fritzbox_smarthome_battery_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
+
+    if section.battery_low is None:
+        return
+
+    _battery_low = {
+        0: 'no',
+        1: 'yes',
+    }
+
+    _message = f'Battery low: {_battery_low.get(section.battery_low, f"unknown ({section.battery_low})")}'
+    if section.battery_low == 0:
+        yield Result(state=State.OK, summary=_message)
+    else:
+        yield Result(state=State(params.get('battery_low', 2)), summary=_message)
+
+
+def check_fritzbox_smarthome_battery_multiple(
+        item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if isinstance(section, Dict):
+        try:
+            yield from check_fritzbox_smarthome_battery_single(params, section[item])
+        except KeyError:
+            return
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_battery_single',
+    service_name='Battery',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_battery_single,
+    check_function=check_fritzbox_smarthome_battery_single,
+    check_ruleset_name='fritzbox_smarthome_battery_single',
+    check_default_parameters={}
+)
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_battery_multiple',
+    service_name='Smarthome Battery %s',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_battery_multiple,
+    check_function=check_fritzbox_smarthome_battery_multiple,
+    check_ruleset_name='fritzbox_smarthome_battery_multiple',
+    check_default_parameters={}
+)
diff --git a/source/agent_based/fritzbox_smarthome_device_lock.py b/source/agent_based/fritzbox_smarthome_device_lock.py
new file mode 100644
index 0000000000000000000000000000000000000000..6c088fd7a7b41578eb49c70221a78d1b59725bd1
--- /dev/null
+++ b/source/agent_based/fritzbox_smarthome_device_lock.py
@@ -0,0 +1,89 @@
+#!/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-30
+# File  : fritzbox_smarthome_device_lock.py (check plugin)
+#
+#
+
+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
+
+
+def discovery_fritzbox_smarthome_device_lock_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        if section.lock is not None:
+            yield Service()
+
+
+def discovery_fritzbox_smarthome_device_lock_multiple(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        for device_id, device in section.items():
+            if device.lock is not None:
+                yield Service(item=str(device_id))
+
+
+def check_fritzbox_smarthome_device_lock_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
+
+    if section.device_lock is None:
+        return
+
+    def _get_status(status: int):
+        _dev_lock = {
+            0: 'is not active',
+            1: 'is active',
+        }
+        return _dev_lock.get(status, f'unknown ({status})')
+
+    yield Result(state=State.OK, summary=f'Button lock on the device {_get_status(section.device_lock)}')
+
+
+def check_fritzbox_smarthome_device_lock_multiple(
+        item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if isinstance(section, Dict):
+        try:
+            yield from check_fritzbox_smarthome_device_lock_single(params, section[item])
+        except KeyError:
+            return
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_device_lock_single',
+    service_name='Device lock',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_device_lock_single,
+    check_function=check_fritzbox_smarthome_device_lock_single,
+    # check_ruleset_name='fritzbox_smarthome_device_lock_single',
+    check_default_parameters={}
+)
+
+register.check_plugin(
+    name='fritzbox_smarthome_device_lock_multiple',
+    service_name='Smarthome Device lock %s',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_device_lock_multiple,
+    check_function=check_fritzbox_smarthome_device_lock_multiple,
+    # check_ruleset_name='fritzbox_smarthome_device_lock_multiple',
+    check_default_parameters={}
+)
diff --git a/source/agent_based/fritzbox_smarthome_power_meter.py b/source/agent_based/fritzbox_smarthome_power_meter.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e1c0968b4c9cf0b14806af2775b0695eecd1311
--- /dev/null
+++ b/source/agent_based/fritzbox_smarthome_power_meter.py
@@ -0,0 +1,239 @@
+#!/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_power_meter.py (check plugin)
+#
+#
+
+from time import time as time_now
+from typing import Dict
+
+from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+    GetRateError,
+    Result,
+    Service,
+    State,
+    check_levels,
+    get_rate,
+    get_value_store,
+    register,
+)
+from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import CheckResult, DiscoveryResult
+from cmk.utils.render import physical_precision
+from cmk.base.plugins.agent_based.utils.fritzbox_smarthome import AvmSmartHomeDevice
+
+
+def discovery_fritzbox_smarthome_voltage_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        if section.power_meter and section.power_meter.voltage:
+            yield Service()
+
+
+def discovery_fritzbox_smarthome_voltage_multiple(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        for device_id, device in section.items():
+            if device.power_meter and device.power_meter.voltage:
+                yield Service(item=str(device_id))
+
+
+def check_fritzbox_smarthome_voltage_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
+    if section.power_meter and section.power_meter.voltage:
+        yield from check_levels(
+            label='Voltage',
+            levels_lower=params.get('levels_lower'),
+            levels_upper=params.get('levels'),
+            metric_name='voltage',
+            render_func=lambda x: f'{x}V',
+            value=section.power_meter.voltage,
+        )
+
+
+def check_fritzbox_smarthome_voltage_multiple(
+        item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if isinstance(section, Dict):
+        try:
+            yield from check_fritzbox_smarthome_voltage_single(params, section[item])
+        except KeyError:
+            return
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_voltage_single',
+    service_name='Voltage',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_voltage_single,
+    check_function=check_fritzbox_smarthome_voltage_single,
+    check_ruleset_name='voltage_single',
+    check_default_parameters={}
+)
+
+register.check_plugin(
+    name='fritzbox_smarthome_voltage_multiple',
+    service_name='Smarthome Voltage %s',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_voltage_multiple,
+    check_function=check_fritzbox_smarthome_voltage_multiple,
+    check_ruleset_name='voltage',
+    check_default_parameters={}
+)
+
+
+def discovery_fritzbox_smarthome_power_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        if section.power_meter and section.power_meter.power:
+            yield Service()
+
+
+def discovery_fritzbox_smarthome_power_multiple(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        for device_id, device in section.items():
+            if device.power_meter and device.power_meter.power:
+                yield Service(item=str(device_id))
+
+
+def check_fritzbox_smarthome_power_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
+
+    if section.power_meter and section.power_meter.power:
+        yield from check_levels(
+            value=section.power_meter.power,
+            metric_name='power',
+            label='Power',
+            render_func=lambda x: f'{x}W',
+            levels_upper=params.get('levels_upper'),
+            levels_lower=params.get('levels_lower'),
+        )
+
+
+def check_fritzbox_smarthome_power_multiple(
+        item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if isinstance(section, Dict):
+        try:
+            yield from check_fritzbox_smarthome_power_single(params, section[item])
+        except KeyError:
+            return
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_power_single',
+    service_name='Power',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_power_single,
+    check_function=check_fritzbox_smarthome_power_single,
+    check_ruleset_name='epower_single',
+    check_default_parameters={}
+)
+
+register.check_plugin(
+    name='fritzbox_smarthome_power_multiple',
+    service_name='Smarthome Power %s',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_power_multiple,
+    check_function=check_fritzbox_smarthome_power_multiple,
+    check_ruleset_name='epower',
+    check_default_parameters={}
+)
+
+
+def discovery_fritzbox_smarthome_energy_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        if section.power_meter and section.power_meter.energy:
+            yield Service()
+
+
+def discovery_fritzbox_smarthome_energy_multiple(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        for device_id, device in section.items():
+            if device.power_meter and device.power_meter.energy:
+                yield Service(item=str(device_id))
+
+
+def check_fritzbox_smarthome_energy_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
+
+    if section.power_meter and section.power_meter.energy:
+        try:
+            energy = get_rate(
+                value_store=get_value_store(),
+                key='energy',
+                time=time_now(),
+                value=section.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"),
+                levels_lower=params.get('levels_lower'),
+                levels_upper=params.get('levels_upper'),
+            )
+
+        yield Result(
+            state=State.OK,
+            summary=f'Consumption total: '
+                    f'{physical_precision(v=section.power_meter.energy, precision=3, unit_symbol="Wh")}'
+        )
+
+
+def check_fritzbox_smarthome_energy_multiple(
+        item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if isinstance(section, Dict):
+        try:
+            yield from check_fritzbox_smarthome_energy_single(params, section[item])
+        except KeyError:
+            return
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_energy_single',
+    service_name='Energy',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_energy_single,
+    check_function=check_fritzbox_smarthome_energy_single,
+    check_ruleset_name='energy_single',
+    check_default_parameters={}
+)
+
+register.check_plugin(
+    name='fritzbox_smarthome_energy_multiple',
+    service_name='Smarthome Energy %s',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_energy_multiple,
+    check_function=check_fritzbox_smarthome_energy_multiple,
+    check_ruleset_name='energy_multiple',
+    check_default_parameters={}
+)
diff --git a/source/agent_based/fritzbox_smarthome_power_socket.py b/source/agent_based/fritzbox_smarthome_power_socket.py
new file mode 100644
index 0000000000000000000000000000000000000000..d702cb982da21eb64265129736411316c179fc0d
--- /dev/null
+++ b/source/agent_based/fritzbox_smarthome_power_socket.py
@@ -0,0 +1,91 @@
+#!/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-30
+# File  : fritzbox_smarthome_power_socket.py (check plugin)
+#
+#
+
+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
+
+
+def discovery_fritzbox_smarthome_power_socket_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        if section.switch is not None:
+            yield Service()
+
+
+def discovery_fritzbox_smarthome_power_socket_multiple(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        for device_id, device in section.items():
+            if device.switch is not None:
+                yield Service(item=str(device_id))
+
+
+def check_fritzbox_smarthome_power_socket_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
+
+    if not section.switch:
+        return
+
+    def _get_status(status: int):
+        _switch_state = {
+            0: 'off',
+            1: 'on',
+        }
+        return _switch_state.get(status, f'unknown ({status})')
+
+    yield Result(state=State.OK, summary=f'State: {_get_status(section.switch.state)}')
+    yield Result(state=State.OK, summary=f'Mode: {section.switch.mode}')
+
+
+def check_fritzbox_smarthome_power_socket_multiple(
+        item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if isinstance(section, Dict):
+        try:
+            yield from check_fritzbox_smarthome_power_socket_single(params, section[item])
+        except KeyError:
+            return
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_power_socket_single',
+    service_name='Power socket',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_power_socket_single,
+    check_function=check_fritzbox_smarthome_power_socket_single,
+    # check_ruleset_name='fritzbox_smarthome_power_socket',
+    check_default_parameters={}
+)
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_power_socket_multiple',
+    service_name='Smarthome Power socket %s',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_power_socket_multiple,
+    check_function=check_fritzbox_smarthome_power_socket_multiple,
+    # check_ruleset_name='fritzbox_smarthome_power_socket',
+    check_default_parameters={}
+)
diff --git a/source/agent_based/fritzbox_smarthome_switch.py b/source/agent_based/fritzbox_smarthome_switch.py
new file mode 100644
index 0000000000000000000000000000000000000000..7bfeac10cc0d0b5123851487a22971cc57557b52
--- /dev/null
+++ b/source/agent_based/fritzbox_smarthome_switch.py
@@ -0,0 +1,91 @@
+#!/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-30
+# File  : fritzbox_smarthome_power_socket.py (check plugin)
+#
+#
+
+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
+
+
+def discovery_fritzbox_smarthome_switch_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        if section.switch is not None:
+            yield Service()
+
+
+def discovery_fritzbox_smarthome_switch_multiple(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        for device_id, device in section.items():
+            if device.simple_on_off is not None:
+                yield Service(item=str(device_id))
+
+
+def check_fritzbox_smarthome_switch_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
+
+    if not section.simple_on_off:
+        return
+
+    if section.simple_on_off:
+        def _get_status(status: int):
+            _simple_onf_off_state = {
+                0: 'off',
+                1: 'on',
+            }
+            return _simple_onf_off_state.get(status, f'unknown ({status})')
+
+        yield Result(state=State.OK, summary=f'State: {_get_status(section.switch.state)}')
+
+
+def check_fritzbox_smarthome_switch_multiple(
+        item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if isinstance(section, Dict):
+        try:
+            yield from check_fritzbox_smarthome_switch_single(params, section[item])
+        except KeyError:
+            return
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_switch_single',
+    service_name='Switch',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_switch_single,
+    check_function=check_fritzbox_smarthome_switch_single,
+    # check_ruleset_name='fritzbox_smarthome_switch',
+    check_default_parameters={}
+)
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_switch_multiple',
+    service_name='Smarthome Switch %s',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_switch_multiple,
+    check_function=check_fritzbox_smarthome_switch_multiple,
+    # check_ruleset_name='fritzbox_smarthome_switch',
+    check_default_parameters={}
+)
diff --git a/source/agent_based/fritzbox_smarthome_temperature.py b/source/agent_based/fritzbox_smarthome_temperature.py
new file mode 100644
index 0000000000000000000000000000000000000000..42875b7c17593e59a6768cede99a60a3133aa6b4
--- /dev/null
+++ b/source/agent_based/fritzbox_smarthome_temperature.py
@@ -0,0 +1,93 @@
+#!/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_temperature.py (check plugin)
+#
+#
+
+from typing import Dict
+
+from cmk.base.plugins.agent_based.agent_based_api.v1 import Result, Service, State, register
+from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import CheckResult, DiscoveryResult
+from cmk.base.plugins.agent_based.utils.temperature import check_temperature, _render_temp_with_unit
+from cmk.base.plugins.agent_based.utils.fritzbox_smarthome import AvmSmartHomeDevice
+
+
+def discovery_fritzbox_smarthome_temperature_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        if section.temperature:
+            yield Service()
+
+
+def discovery_fritzbox_smarthome_temperature_multiple(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        for device_id, device in section.items():
+            if device.temperature:
+                yield Service(item=str(device_id))
+
+
+def check_fritzbox_smarthome_temperature_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
+
+    if not section.temperature:
+        return
+
+    yield from check_temperature(
+        reading=section.temperature.celsius,
+        params=params,
+    )
+    if section.temperature.offset != 0:
+        _status = section.temperature.celsius + section.temperature.offset * -1
+        _message = (
+            f'Temperature measured at the thermostat: '
+            f'{_render_temp_with_unit(_status, params.get("output_unit", "c"))}'
+        )
+        yield Result(state=State.OK, notice=_message)
+        yield Result(
+            state=State.OK,
+            notice=f'Temperature offset: '
+                   f'{_render_temp_with_unit(section.temperature.offset, params.get("output_unit", "c"))}'
+        )
+
+
+def check_fritzbox_smarthome_temperature_multiple(
+        item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if isinstance(section, Dict):
+        try:
+            yield from check_fritzbox_smarthome_temperature_single(params, section[item])
+        except KeyError:
+            return
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_temperature_single',
+    service_name='Temperature',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_temperature_single,
+    check_function=check_fritzbox_smarthome_temperature_single,
+    check_ruleset_name='temperature_single',
+    check_default_parameters={}
+)
+
+register.check_plugin(
+    name='fritzbox_smarthome_temperature',
+    service_name='Smarthome Temperature %s',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_temperature_multiple,
+    check_function=check_fritzbox_smarthome_temperature_multiple,
+    check_ruleset_name='temperature',
+    check_default_parameters={}
+)
diff --git a/source/agent_based/fritzbox_smarthome_thermostat.py b/source/agent_based/fritzbox_smarthome_thermostat.py
new file mode 100644
index 0000000000000000000000000000000000000000..2579f61cc82f0cece617d8049186e270016959b9
--- /dev/null
+++ b/source/agent_based/fritzbox_smarthome_thermostat.py
@@ -0,0 +1,148 @@
+#!/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_thermostat.py (check plugin)
+#
+#
+
+from time import strftime, localtime
+from typing import Dict
+
+from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+    Metric,
+    Result,
+    Service,
+    State,
+    register,
+)
+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
+
+_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
+
+
+def discovery_fritzbox_smarthome_thermostat_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        if section.thermostat:
+            yield Service()
+
+
+def discovery_fritzbox_smarthome_thermostat_multiple(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        for device_id, device in section.items():
+            if device.thermostat:
+                yield Service(item=str(device_id))
+
+
+def check_fritzbox_smarthome_thermostat_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
+
+    if not section.thermostat:
+        return
+
+    if thermostat := section.thermostat:
+        _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',
+        }
+
+        if thermostat.temp_target == 126.5:  # == radiator off
+            yield Result(state=State.OK, summary=f'Temperature current: {thermostat.temp_current}°C')
+            yield Result(state=State.OK, summary=f'Temperature target: radiator off')
+        else:
+            deviation = thermostat.temp_current - thermostat.temp_target
+            if deviation == 0:
+                yield Result(state=State.OK, summary=f'Temperature current: {thermostat.temp_target}°C')
+            else:
+                _message = f'Temperature current: {thermostat.temp_current}°C (deviation from target {deviation}°C)'
+                _state = State.OK
+                if params.get('deviation'):
+                    warn, crit = params['deviation']
+                    if abs(deviation) >= crit:
+                        _state = State.CRIT
+                    elif abs(deviation) >= warn:
+                        _state = State.WARN
+                yield Result(state=_state, summary=_message)
+
+            yield Result(
+                state=State.OK,
+                summary=f'Target: {thermostat.temp_target}°C',
+                details=f'Temperature target: {thermostat.temp_target}°C'
+            )
+            yield Metric(name='temp_target', value=thermostat.temp_target)
+
+        yield Result(state=State.OK, notice=f'Temperature economic: {thermostat.temp_economic}°C')
+        yield Result(state=State.OK, notice=f'Temperature comfort: {thermostat.temp_comfort}°C')
+
+        yield Metric(name='temp_current', value=thermostat.temp_current)
+        yield Metric(name='temp_comfort', value=thermostat.temp_comfort)
+        yield Metric(name='temp_economic', value=thermostat.temp_economic)
+
+        if thermostat.next_change:
+            yield Result(
+                state=State.OK,
+                notice=f'End of period: {strftime(_TIME_FORMAT, localtime(thermostat.next_change.end_period))}'
+            )
+            yield Result(
+                state=State.OK,
+                notice=f'Temperature target after end of period: {thermostat.next_change.temp_change_to}°C'
+            )
+
+        _message = f'Error code: {_error_codes.get(thermostat.error_code, f"unknown error {thermostat.error_code}")}'
+        if thermostat.error_code == 0:
+            yield Result(state=State.OK, notice=_message)
+        else:
+            yield Result(
+                state=State(params.get('state_on_error', 1)),
+                summary=f'Error Code: {thermostat.error_code} (see details)',
+                details=_message)
+
+
+def check_fritzbox_smarthome_thermostat_multiple(
+        item, params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if isinstance(section, Dict):
+        try:
+            yield from check_fritzbox_smarthome_thermostat_single(params, section[item])
+        except KeyError:
+            return
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_thermostat_single',
+    service_name='Thermostat',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_thermostat_single,
+    check_function=check_fritzbox_smarthome_thermostat_single,
+    check_ruleset_name='fritzbox_smarthome_thermostat_single',
+    check_default_parameters={}
+)
+
+
+register.check_plugin(
+    name='fritzbox_smarthome_thermostat_multiple',
+    service_name='Smarthome Thermostat %s',
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_thermostat_multiple,
+    check_function=check_fritzbox_smarthome_thermostat_multiple,
+    check_ruleset_name='fritzbox_smarthome_thermostat_multiple',
+    check_default_parameters={}
+)
diff --git a/source/agent_based/inv_fritzbox_smarthome.py b/source/agent_based/inv_fritzbox_smarthome.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f77bf5b73d0c1de58c04821adcecfeb1595adf5
--- /dev/null
+++ b/source/agent_based/inv_fritzbox_smarthome.py
@@ -0,0 +1,46 @@
+#!/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-29
+# File  : inv_fritzbox_smarthome.py (inventory plugin)
+#
+
+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
+
+
+def _add_avm_smarthome_device(device: AvmSmartHomeDevice):
+    path = ['hardware', 'avm', 'smart_home_devices']
+    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)
+        }
+    )
+
+
+def inventory_fritzbox_smarthome(section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]) -> InventoryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        yield from _add_avm_smarthome_device(device=section)
+    else:
+        for device in section.values():
+            yield from _add_avm_smarthome_device(device)
+
+
+register.inventory_plugin(
+    name="inv_fritzbox_smarthome",
+    sections=['fritzbox_smarthome'],
+    inventory_function=inventory_fritzbox_smarthome,
+)
diff --git a/source/agent_based/utils/fritzbox_smarthome.py b/source/agent_based/utils/fritzbox_smarthome.py
new file mode 100644
index 0000000000000000000000000000000000000000..b6a9137d4fd0e3e03c0ef29975b1cec5f622fc9c
--- /dev/null
+++ b/source/agent_based/utils/fritzbox_smarthome.py
@@ -0,0 +1,238 @@
+#!/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-29
+# File  : fritzbox_smarthome.py (check plugin utils)
+#
+# Based on the work of Maximilian Clemens, see https://github.com/MaximilianClemens/checkmk_fritzbox
+#
+#
+
+from dataclasses import dataclass
+from typing import Any, List, Dict
+
+
+@dataclass(frozen=True)
+class AvmTemperature:
+    celsius: float
+    offset: float
+
+
+@dataclass(frozen=True)
+class AvmAlert:
+    last_alert_chg_timestamp: int
+    state: int
+
+
+@dataclass(frozen=True)
+class AvmButton:
+    last_pressed_timestamp: int
+    id: int | None = None
+    identifier: int | None = None
+    name: str | None = None
+
+
+@dataclass(frozen=True)
+class AvmPowerMeter:
+    energy: float
+    power: float
+    voltage: float
+
+
+@dataclass(frozen=True)
+class AvmSimpleOnOff:
+    state: int
+
+
+@dataclass(frozen=True)
+class AvmNextChange:
+    end_period: int
+    temp_change_to: float
+
+
+@dataclass(frozen=True)
+class AvmThermostat:
+    error_code: int
+    temp_comfort: float
+    temp_current: float
+    temp_economic: float
+    temp_target: float
+    adaptive_heating_active: int | None = None
+    adaptive_heating_running: int | None = None
+    battery: float | None = None
+    boost_active: int | None = None
+    boost_active_end_time: int | None = None
+    holiday_active: int | None = None
+    next_change: AvmNextChange | None = None
+    summer_active: int | None = None
+    window_open_activ: int | None = None
+
+
+@dataclass(frozen=True)
+class AvmSwitch:
+    mode: str
+    state: int
+
+
+@dataclass(frozen=True)
+class AvmSmartHomeDevice:
+    fbm: int
+    functions: List[str]
+    fw_version: str
+    id: str
+    identifier: str
+    manufacturer: str
+    name: str
+    present: int
+    product_name: str
+    battery_low: int | None = None
+    device_lock: int | None = None
+    lock: int | None = None
+    power_meter: AvmPowerMeter | None = None
+    simple_on_off: AvmSimpleOnOff | None = None
+    switch: AvmSwitch | None = None
+    temperature: AvmTemperature | None = None
+    thermostat: AvmThermostat | None = None
+    tx_busy: int | None = None
+
+
+_AVM_THERMOSTAT = 'hkr'
+_AVM_SWITCH = 'switch'
+_AVM_POWER_METER = 'powermeter'
+_AVM_TEMPERATURE = 'temperature'
+_AVM_SIMPLE_ON_OFF = 'simpleonoff'
+_AVM_NEXT_CHANGE = 'nextchange'
+
+
+def _get_battery_low(device: Dict[str, Any]) -> int | None:
+    try:
+        return int(device[_AVM_THERMOSTAT]['batterylow'])
+    except KeyError:
+        pass
+
+    return None
+
+
+def _get_lock(device: Dict[str, Any]) -> int | None:
+    try:
+        return int(device[_AVM_THERMOSTAT]['lock'])
+    except KeyError:
+        pass
+
+    try:
+        return int(device[_AVM_SWITCH]['lock'])
+    except KeyError:
+        pass
+
+    return None
+
+
+def _get_device_lock(device: Dict[str, Any]) -> int | None:
+    try:
+        return int(device[_AVM_THERMOSTAT]['devicelock'])
+    except KeyError:
+        pass
+
+    try:
+        return int(device[_AVM_SWITCH]['devicelock'])
+    except KeyError:
+        pass
+
+    return None
+
+
+def parse_avm_smarthome_device(raw_device: Dict[str, Any]) -> AvmSmartHomeDevice:
+    return AvmSmartHomeDevice(
+            battery_low=_get_battery_low(raw_device),
+            device_lock=_get_device_lock(raw_device),
+            fbm=int(raw_device['functionbitmask']),
+            functions=get_avm_device_functions_from_fbm(int(raw_device['functionbitmask'])),
+            fw_version=str(raw_device['fwversion']),
+            id=str(raw_device['id']),
+            identifier=str(raw_device['identifier']),
+            lock=_get_lock(raw_device),
+            manufacturer=str(raw_device['manufacturer']),
+            name=str(raw_device['name']),
+            present=int(raw_device['present']),
+            product_name=str(raw_device['productname']),
+            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,
+            thermostat=AvmThermostat(
+                temp_current=float(raw_device[_AVM_THERMOSTAT]['tist']) / 2.0,
+                temp_target=float(raw_device[_AVM_THERMOSTAT]['tsoll']) / 2.0,
+                temp_economic=float(raw_device[_AVM_THERMOSTAT]['absenk']) / 2.0,
+                temp_comfort=float(raw_device[_AVM_THERMOSTAT]['komfort']) / 2.0,
+                error_code=int(raw_device[_AVM_THERMOSTAT]['errorcode']),
+                next_change=AvmNextChange(
+                    end_period=int(raw_device[_AVM_THERMOSTAT][_AVM_NEXT_CHANGE]['endperiod']),
+                    temp_change_to=float(raw_device[_AVM_THERMOSTAT][_AVM_NEXT_CHANGE]['tchange']) / 2.0,
+                ) if raw_device[_AVM_THERMOSTAT].get(_AVM_NEXT_CHANGE) else None,
+            ) if raw_device.get(_AVM_THERMOSTAT) else None,
+            switch=AvmSwitch(
+                state=int(raw_device[_AVM_SWITCH]['state']),
+                mode=str(raw_device[_AVM_SWITCH]['mode']),
+            ) 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,
+        )
+
+
+def get_avm_device_functions_from_fbm(fbm: int) -> List[str]:
+    functions = []
+    if fbm >> 0 & 1:
+        functions.append('HAN-FUN Device')
+    if fbm >> 2 & 1:
+        functions.append('Light')
+    if fbm >> 3 & 1:
+        functions.append('unknown (Bit 3)')
+    if fbm >> 4 & 1:
+        functions.append('Alarm Sensor')
+    if fbm >> 5 & 1:
+        functions.append('AVM Button')
+    if fbm >> 6 & 1:
+        functions.append('AVM Thermostat')
+    if fbm >> 7 & 1:
+        functions.append('AVM Powermeter')
+    if fbm >> 8 & 1:
+        functions.append('Temperature Sensor')
+    if fbm >> 9 & 1:
+        functions.append('AVM Switching socket')
+    if fbm >> 10 & 1:
+        functions.append('AVM DECT Repeater')
+    if fbm >> 11 & 1:
+        functions.append('AVM Microphone')
+    if fbm >> 12 & 1:
+        functions.append('unknown (Bit 12)')
+    if fbm >> 13 & 1:
+        functions.append('HAN-FUN Unit')
+    if fbm >> 14 & 1:
+        functions.append('unknown (Bit 14)')
+    if fbm >> 15 & 1:
+        functions.append('on/off switchable device')
+    if fbm >> 16 & 1:
+        functions.append('Device with adjustable dimming, height and level')
+    if fbm >> 17 & 1:
+        functions.append('Light')
+    if fbm >> 18 & 1:
+        functions.append('Roller shutter')
+    if fbm >> 19 & 1:
+        functions.append('unknown (Bit 19)')
+    if fbm >> 20 & 1:
+        functions.append('Humidity sensor')
+
+    functions.sort()
+
+    return functions
diff --git a/source/agents/special/agent_fritzbox_smarthome b/source/agents/special/agent_fritzbox_smarthome
new file mode 100755
index 0000000000000000000000000000000000000000..48c63127519dda4b07ce0bc284eab2517e47dee4
--- /dev/null
+++ b/source/agents/special/agent_fritzbox_smarthome
@@ -0,0 +1,7 @@
+#!/usr/bin/env python3
+import sys
+
+from cmk.special_agents.agent_fritzbox_smarthome import main
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/source/checkman/fritzbox_smarthome b/source/checkman/fritzbox_smarthome
new file mode 100644
index 0000000000000000000000000000000000000000..2c6df2a45ae403004c2582d87869b5b437cbeebf
--- /dev/null
+++ b/source/checkman/fritzbox_smarthome
@@ -0,0 +1,11 @@
+title: Fritz!Box Smarthome
+agents: fritzbox_smarthome
+catalog: hw/network/avm
+license: GPL
+distribution: check_mk
+description:
+ 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:
+ fritzbox smarthome devices
diff --git a/source/checks/agent_fritzbox_smarthome b/source/checks/agent_fritzbox_smarthome
new file mode 100644
index 0000000000000000000000000000000000000000..26a785476309e6f6aae7e43c7d47bd8579f0f397
--- /dev/null
+++ b/source/checks/agent_fritzbox_smarthome
@@ -0,0 +1,44 @@
+#!/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 (piggyback := params.get("piggyback")) is not None:
+    #     args.append("--piggyback")
+
+    if (prefix := params.get("prefix")) is not None:
+        args.extend(["--prefix"] + [hostname])
+
+    if (testing := params.get("testing")) is not None:
+        args.append("--testing")
+
+    if (no_piggyback := params.get("no_piggyback")) is not None:
+        args.append("--no-piggyback")
+
+    return args
+
+
+special_agent_info['fritzbox_smarthome'] = agent_fritzbox_smarthome_arguments
diff --git a/source/gui/metrics/fritzbox_smarthome.py b/source/gui/metrics/fritzbox_smarthome.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a8fc1255de4954df05fb55b76f73ae7034464d6
--- /dev/null
+++ b/source/gui/metrics/fritzbox_smarthome.py
@@ -0,0 +1,80 @@
+#!/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,
+    perfometer_info,
+)
+
+check_metrics["check_mk-fritzbox_smarthome_thermostat_single"] = {
+    "temp_current": {"auto_graph": False},
+    "temp_target": {"auto_graph": False},
+    "temp_economic": {"auto_graph": False},
+    "temp_comfort": {"auto_graph": False},
+}
+
+check_metrics["check_mk-fritzbox_smarthome_thermostat_multiple"] = {
+    "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": "21/a",
+    "unit": "c",
+}
+metric_info["temp_economic"] = {
+    "title": _("Temperature economic"),
+    "color": "31/a",
+    "unit": "c",
+}
+metric_info["temp_comfort"] = {
+    "title": _("Temperature comfort"),
+    "color": "11/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"),
+    ],
+    "optional_metrics": [
+        "temp_target",
+    ],
+}
+
+perfometer_info.append(('stacked', [
+    {
+        'type': 'linear',
+        'segments': ['temp_current'],
+        'total': 50,
+    },
+    {
+        'type': 'linear',
+        'segments': ['temp_target'],
+        'total': 50,
+    }
+]))
diff --git a/source/gui/wato/check_parameters/electrical_energy.py b/source/gui/wato/check_parameters/electrical_energy.py
new file mode 100644
index 0000000000000000000000000000000000000000..737f69ae25d7f62b7be6054d2fd7d101babd8f2f
--- /dev/null
+++ b/source/gui/wato/check_parameters/electrical_energy.py
@@ -0,0 +1,76 @@
+#!/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-29
+# File  : electrical_energy.py (WATO)
+
+from cmk.gui.i18n import _
+from cmk.gui.plugins.wato.utils import (
+    CheckParameterRulespecWithItem,
+    CheckParameterRulespecWithoutItem,
+    rulespec_registry,
+    RulespecGroupCheckParametersEnvironment,
+)
+from cmk.gui.valuespec import Dictionary, Integer, TextInput, Tuple
+
+
+def _item_spec_energy():
+    return TextInput(
+        title=_("Phase"), help=_("The identifier of the phase the power is related to.")
+    )
+
+
+def _parameter_valuespec_energy():
+    return Dictionary(
+        title=_('Parameters'),
+        elements=[
+            (
+                "levels_upper",
+                Tuple(
+                    title=_("Upper levels for electrical energy"),
+                    elements=[
+                        Integer(title=_("warning at"), unit="Wh"),
+                        Integer(title=_("critical at"), unit="Wh"),
+                    ],
+                ),
+            ),
+            (
+                "levels_lower",
+                Tuple(
+                    title=_("Lower levels for electrical energy"),
+                    elements=[
+                        Integer(title=_("warning if below"), unit="Wh"),
+                        Integer(title=_("critical if below"), unit="Wh"),
+                    ],
+                ),
+            ),
+        ],
+        help=_(
+            "Levels for the electrical energy consumption of a device "
+            "like a UPS or a PDU. Several phases may be addressed independently."
+        ),
+    )
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name="energy_multiple",
+        group=RulespecGroupCheckParametersEnvironment,
+        item_spec=_item_spec_energy,
+        parameter_valuespec=_parameter_valuespec_energy,
+        title=lambda: TextInput(title=_("Electrical Energy (several phases)")),
+    )
+)
+
+rulespec_registry.register(
+    CheckParameterRulespecWithoutItem(
+        check_group_name="energy_single",
+        group=RulespecGroupCheckParametersEnvironment,
+        parameter_valuespec=_parameter_valuespec_energy,
+        title=lambda: _("Electrical Energy (single phase)"),
+    )
+)
\ No newline at end of file
diff --git a/source/gui/wato/check_parameters/epower.py b/source/gui/wato/check_parameters/epower.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5958163afc4378f683fad50d580c97262e4931b
--- /dev/null
+++ b/source/gui/wato/check_parameters/epower.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+# Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
+# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
+# conditions defined in the file COPYING, which is part of this source code package.
+
+from cmk.gui.i18n import _
+from cmk.gui.plugins.wato.utils import (
+    CheckParameterRulespecWithItem,
+    CheckParameterRulespecWithoutItem,
+    rulespec_registry,
+    RulespecGroupCheckParametersEnvironment,
+)
+from cmk.gui.valuespec import Dictionary, Integer, Migrate, TextInput, Tuple
+
+
+def _item_spec_epower():
+    return TextInput(
+        title=_("Phase"), help=_("The identifier of the phase the power is related to.")
+    )
+
+
+def _migrate(value: tuple | dict) -> dict:
+    if isinstance(value, tuple):
+        return {"levels_lower": value}
+    return value
+
+
+def _parameter_valuespec_epower():
+    return Migrate(
+        Dictionary(
+            title=_("Parameters"),
+            elements=[
+                (
+                    "levels_lower",
+                    Tuple(
+                        title=_("Lower levels for electrical power"),
+                        elements=[
+                            Integer(title=_("warning if below"), unit="Watt"),
+                            Integer(title=_("critical if below"), unit="Watt"),
+                        ],
+                    ),
+                ),
+                (
+                    "levels_upper",
+                    Tuple(
+                        title=_("Upper levels for electrical power"),
+                        elements=[
+                            Integer(title=_("warning at"), unit="Watt"),
+                            Integer(title=_("critical at"), unit="Watt"),
+                        ],
+                    ),
+                ),
+            ],
+            help=_(
+                "Levels for the electrical power consumption of a device "
+                "like a UPS or a PDU. Several phases may be addressed independently."
+            ),
+        ),
+        migrate=_migrate,
+    )
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name="epower",
+        group=RulespecGroupCheckParametersEnvironment,
+        item_spec=_item_spec_epower,
+        parameter_valuespec=_parameter_valuespec_epower,
+        title=lambda: _("Electrical Power"),
+    )
+)
+
+rulespec_registry.register(
+    CheckParameterRulespecWithoutItem(
+        check_group_name="epower_single",
+        group=RulespecGroupCheckParametersEnvironment,
+        parameter_valuespec=_parameter_valuespec_epower,
+        title=lambda: TextInput(title=_("Electrical Power (single phase)")),
+    )
+)
diff --git a/source/gui/wato/check_parameters/fritzbox_smarthome.py b/source/gui/wato/check_parameters/fritzbox_smarthome.py
new file mode 100644
index 0000000000000000000000000000000000000000..a102e0e302422008fe6265402ee038d19651bb58
--- /dev/null
+++ b/source/gui/wato/check_parameters/fritzbox_smarthome.py
@@ -0,0 +1,139 @@
+#!/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 check plugin)
+#
+
+
+from cmk.gui.i18n import _
+from cmk.gui.valuespec import (
+    Dictionary,
+    Integer,
+    MonitoringState,
+    Tuple,
+    TextInput,
+)
+from cmk.gui.plugins.wato.utils import (
+    CheckParameterRulespecWithItem,
+    CheckParameterRulespecWithoutItem,
+    RulespecGroupCheckParametersApplications,
+    rulespec_registry,
+)
+
+
+def _parameter_valuespec_fritzbox_smarthome():
+    return Dictionary(
+        title=_('Parameter'),
+        elements=[
+            ('present',
+             MonitoringState(
+                 title=_('Monitoring state for offline devices'),
+                 default_value=1,
+             )),
+        ],
+    )
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithoutItem(
+        check_group_name="fritzbox_smarthome_single",
+        group=RulespecGroupCheckParametersApplications,
+        match_type="dict",
+        parameter_valuespec=_parameter_valuespec_fritzbox_smarthome,
+        title=lambda: _('Fritz!Box Smarthome Devices')
+    )
+)
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name="fritzbox_smarthome_multiple",
+        group=RulespecGroupCheckParametersApplications,
+        match_type="dict",
+        parameter_valuespec=_parameter_valuespec_fritzbox_smarthome,
+        title=lambda: _('Fritz!Box Smarthome Devices (with Device-ID)'),
+        item_spec=lambda: TextInput(title=_('Device-ID')),
+    )
+)
+
+
+def _parameter_valuespec_fritzbox_smarthome_thermostat():
+    return Dictionary(
+        title=_('Parameter'),
+        elements=[
+            ('deviation',
+             Tuple(
+                 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')),
+                 ])),
+            ('state_on_error',
+             MonitoringState(
+                 title=_('Monitoring state on error'),
+                 default_value=1,
+             )),
+        ],
+    )
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithoutItem(
+        check_group_name="fritzbox_smarthome_thermostat_single",
+        group=RulespecGroupCheckParametersApplications,
+        match_type="dict",
+        parameter_valuespec=_parameter_valuespec_fritzbox_smarthome_thermostat,
+        title=lambda: _('Fritz!Box Smarthome Thermostat'),
+    )
+)
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name="fritzbox_smarthome_thermostat_multiple",
+        group=RulespecGroupCheckParametersApplications,
+        match_type="dict",
+        parameter_valuespec=_parameter_valuespec_fritzbox_smarthome_thermostat,
+        title=lambda: _('Fritz!Box Smarthome Thermostat (with Device-ID)'),
+        item_spec=lambda: TextInput(title=_('Device-ID')),
+    )
+)
+
+
+def _parameter_valuespec_fritzbox_smarthome_battery():
+    return Dictionary(
+        title=_('Parameter'),
+        elements=[
+            ('battery_low',
+             MonitoringState(
+                 title=_('Monitoring state on low battery'),
+                 default_value=2,
+             )),
+        ],
+    )
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithoutItem(
+        check_group_name="fritzbox_smarthome_battery_single",
+        group=RulespecGroupCheckParametersApplications,
+        match_type="dict",
+        parameter_valuespec=_parameter_valuespec_fritzbox_smarthome_battery,
+        title=lambda: _('Fritz!Box Smarthome battery')
+    )
+)
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        check_group_name="fritzbox_smarthome_battery_multiple",
+        group=RulespecGroupCheckParametersApplications,
+        match_type="dict",
+        parameter_valuespec=_parameter_valuespec_fritzbox_smarthome_battery,
+        title=lambda: _('Fritz!Box Smarthome battery (with Device-ID)'),
+        item_spec=lambda: TextInput(title=_('Device-ID')),
+    )
+)
diff --git a/source/gui/wato/check_parameters/temperature_single.py b/source/gui/wato/check_parameters/temperature_single.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c9d88797128a4367acbb287ca580b4c5f8bf5f3
--- /dev/null
+++ b/source/gui/wato/check_parameters/temperature_single.py
@@ -0,0 +1,28 @@
+#!/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-29
+# File  : temperature_single.py (WATO)
+
+from cmk.gui.i18n import _
+from cmk.gui.plugins.wato.utils import (
+    CheckParameterRulespecWithoutItem,
+    rulespec_registry,
+    RulespecGroupCheckParametersEnvironment,
+)
+
+from cmk.gui.plugins.wato.check_parameters.temperature import _parameter_valuespec_temperature
+
+rulespec_registry.register(
+    CheckParameterRulespecWithoutItem(
+        check_group_name="temperature_single",
+        group=RulespecGroupCheckParametersEnvironment,
+        match_type="dict",
+        parameter_valuespec=_parameter_valuespec_temperature,
+        title=lambda: _("Temperature (without Sensor-ID)"),
+    )
+)
\ No newline at end of file
diff --git a/source/gui/wato/check_parameters/voltage_single.py b/source/gui/wato/check_parameters/voltage_single.py
new file mode 100644
index 0000000000000000000000000000000000000000..7fd6761a10438b84ac57cd446909475e456d6cc8
--- /dev/null
+++ b/source/gui/wato/check_parameters/voltage_single.py
@@ -0,0 +1,28 @@
+#!/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-29
+# File  : voltage_single.py (WATO)
+
+from cmk.gui.i18n import _
+from cmk.gui.plugins.wato.utils import (
+    CheckParameterRulespecWithoutItem,
+    rulespec_registry,
+    RulespecGroupCheckParametersEnvironment,
+)
+
+from cmk.gui.plugins.wato.check_parameters.voltage import _parameter_valuespec_voltage
+
+rulespec_registry.register(
+    CheckParameterRulespecWithoutItem(
+        check_group_name="voltage_single",
+        group=RulespecGroupCheckParametersEnvironment,
+        match_type="dict",
+        parameter_valuespec=_parameter_valuespec_voltage,
+        title=lambda: _("Voltage Sensor (without Sensor-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
new file mode 100644
index 0000000000000000000000000000000000000000..b6696503e4188220a6e1d5ce6a2e53ca2ee23581
--- /dev/null
+++ b/source/lib/python3/cmk/special_agents/agent_fritzbox_smarthome.py
@@ -0,0 +1,284 @@
+#!/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
+import traceback
+import ssl
+import json
+import time
+
+from urllib.request import urlopen
+import argparse
+import xml.etree.ElementTree as ET
+import hashlib
+from re import sub as re_sub
+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\n'
+                    'This is an additional check_MK Fritz!Box Agent which can gather information\'s over the \n'
+                    'AVM AHA HTTP Interface about SmartHome Devices connected to an Fritz!Box.',
+        formatter_class=argparse.RawTextHelpFormatter,
+    )
+    parser.add_argument(
+        'host',
+        help='Host name or IP address of your Fritz!Box',
+    )
+    parser.add_argument(
+        '--debug', action='store_true', default=False,
+        help='Debug mode: let Python exceptions come through',
+    )
+    parser.add_argument(
+        '--ignore_ssl', action='store_true', default=False,
+        help='Tha agent will ignores SSL errors',
+    )
+    parser.add_argument(
+        '--no-piggyback', action='store_true', default=False,
+        help='By default the agent generates the output as piggyback data for each\n'
+             'Samrthome device. If you want to attach all your Smarthome devices directly\n'
+             ' to your Fritz!Box use this option.',
+    )
+    parser.add_argument(
+        '--password', nargs='?',
+        help='The password to logon the Fritz!Box',
+    )
+    parser.add_argument(
+        '--username', nargs='?',
+        help='The username to logon to the Fritz!Box',
+    )
+    parser.add_argument(
+        '--port', nargs='?', type=int, default=443,
+        help='The TCP port on witch to access the Fritz!Box',
+    )
+    parser.add_argument(
+        '--prefix', nargs='?',
+        help='The prefix is used to group all the Smarthome devices from one Fritz!Box in CMK.'
+        )
+    parser.add_argument(
+        '--protocol', nargs='?', choices=['http', 'https'], default='https',
+        help='The protocol used to access the Fritz!Box',
+    )
+
+    parser.add_argument(
+        '--testing', action='store_true', default=False,
+        help='Development usage only (might be ignored)'
+    )
+    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
+    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_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"
+                }
+            }
+        }
+
+        energy = int(__switch_01["powermeter"]["energy"])
+        power = int(__switch_01["powermeter"]["power"])
+        start_time = 1703883617
+        energy_up = int(time.time() - start_time) / 3600 * (int(power) / 1000)
+        __switch_01["powermeter"]["energy"] = str(int(energy + energy_up))
+
+        devices.append(__switch_01)
+        devices.append(__repeater_01)
+        devices.append(__repeater_02)
+        devices.append(__thermostat_01)
+
+    for xml_device in xml_devicelist.findall('device'):
+        devices.append(parse_xml_to_json(xml_device))
+
+    if args.no_piggyback:
+        sys.stdout.write('<<<fritzbox_smarthome:sep(0)>>>\n')
+        # if len(devices) == 1:
+        #     sys.stdout.write(json.dumps(devices[0]))  # single device
+        # else:
+        sys.stdout.write(json.dumps(devices))
+        sys.stdout.write('\n')
+    else:
+        for json_device in devices:
+            name = json_device["name"].replace(' ', '_')
+            name = re_sub(r'[^.\-_a-zA-Z0-9]', '', name)
+            if args.prefix:
+                name = f'{args.prefix}-{name}'
+            sys.stdout.write(f'<<<<{name}>>>>\n')
+            sys.stdout.write('<<<fritzbox_smarthome:sep(0)>>>\n')
+            sys.stdout.write(json.dumps(json_device))
+            sys.stdout.write('\n')
+
+
+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)
diff --git a/source/packages/fritzbox_smarthome b/source/packages/fritzbox_smarthome
new file mode 100644
index 0000000000000000000000000000000000000000..89a5584d6efd6638ca68908f10753cb91b674a6c
--- /dev/null
+++ b/source/packages/fritzbox_smarthome
@@ -0,0 +1,55 @@
+{'author': 'Th.L. (thl-cmk[at]outlook[dot]com)',
+ 'description': 'Agent and checks/inventory for Fritz!Box smart home devices\n'
+                '\n'
+                'This package is based on the work of  Maximilian Clemens, '
+                'see \n'
+                'https://github.com/MaximilianClemens/checkmk_fritzbox\n'
+                '\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'
+                '\n'
+                'FRITZ!DECT Repeater 100\n'
+                'FRITZ!DECT 200\n'
+                'FRITZ!DECT 302\n'
+                '\n'
+                'So 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'
+                'Also, my FRIT!BOX is not brand new, so it may not include all '
+                'the features that the smart home \n'
+                'devices support.  E.g. window open/close for the FRITZ!DECT '
+                '302.\n'
+                '\n',
+ 'download_url': 'https://github.com/MaximilianClemens/checkmk_fritzbox',
+ 'files': {'agent_based': ['fritzbox_smarthome.py',
+                           'fritzbox_smarthome_power_meter.py',
+                           'fritzbox_smarthome_temperature.py',
+                           'fritzbox_smarthome_thermostat.py',
+                           'inv_fritzbox_smarthome.py',
+                           'utils/fritzbox_smarthome.py',
+                           'fritzbox_smarthome_battery.py',
+                           'fritzbox_smarthome_app_lock.py',
+                           'fritzbox_smarthome_device_lock.py',
+                           'fritzbox_smarthome_power_socket.py',
+                           'fritzbox_smarthome_switch.py'],
+           'agents': ['special/agent_fritzbox_smarthome'],
+           'checkman': ['fritzbox_smarthome'],
+           'checks': ['agent_fritzbox_smarthome'],
+           'gui': ['wato/check_parameters/electrical_energy.py',
+                   'wato/check_parameters/epower.py',
+                   'wato/check_parameters/fritzbox_smarthome.py',
+                   'metrics/fritzbox_smarthome.py',
+                   'wato/check_parameters/temperature_single.py',
+                   'wato/check_parameters/voltage_single.py'],
+           'lib': ['python3/cmk/special_agents/agent_fritzbox_smarthome.py'],
+           'web': ['plugins/wato/agent_fritzbox_smarthome.py',
+                   'plugins/views/fritzbox_smarthome.py']},
+ 'name': 'fritzbox_smarthome',
+ 'title': 'Fritz!Box SmartHome',
+ 'version': '0.8.3-20231230',
+ 'version.min_required': '2.2.0b1',
+ 'version.packaged': '2.2.0p14',
+ 'version.usable_until': None}
diff --git a/source/web/plugins/views/fritzbox_smarthome.py b/source/web/plugins/views/fritzbox_smarthome.py
new file mode 100644
index 0000000000000000000000000000000000000000..76733ecaa3209fc5d02fec3438979d4717bb926a
--- /dev/null
+++ b/source/web/plugins/views/fritzbox_smarthome.py
@@ -0,0 +1,40 @@
+#!/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')},
+})
diff --git a/source/web/plugins/wato/agent_fritzbox_smarthome.py b/source/web/plugins/wato/agent_fritzbox_smarthome.py
new file mode 100644
index 0000000000000000000000000000000000000000..cc2f86736981940299dbac01368d26e0920ba7d8
--- /dev/null
+++ b/source/web/plugins/wato/agent_fritzbox_smarthome.py
@@ -0,0 +1,91 @@
+#!/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='',
+                title=_('Ignore SSL errors'),
+            )),
+            ('prefix', FixedValue(
+                value=True,
+                help='Uses the hostname of the Fritz!Box as prefix for the hostnames generated for piggyback',
+                totext='',
+                title=_('Add Prefix'),
+            )),
+            ('no_piggyback', FixedValue(
+                value=True,
+                help='The agent will not generate piggyback data. '
+                     'The Smarthome devices will be attached to the this host.',
+                totext='',
+                title=_('Disable piggyback'),
+            )),
+            ('testing', FixedValue(
+                value=True,
+                help='Development only, will be (most likely) ignored in production :-)',
+                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,
+    ))