diff --git a/README.md b/README.md
index 3f09eb98f76c44b1d383a29b3d51e4218b33cf4f..8a58ce76181a22a9aaf1dd6ca1f25f972e57ea43 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[PACKAGE]: ../../raw/master/mkp/fritzbox_smarthome-0.7.0-20231228.mkp "fritzbox_smarthome-0.7.0-20231228.mkp"
+[PACKAGE]: ../../raw/master/mkp/fritzbox_smarthome-0.8.2-20231230.mkp "fritzbox_smarthome-0.8.2-20231230.mkp"
 # Title
 
 A short description about the plugin
diff --git a/agent_based/fritzbox_smarthome.py b/agent_based/fritzbox_smarthome.py
index fabe3576173dcd9f03e7eca4708bdfa25e159e94..1159ac6437732117d1a9136e30a569bd29ea5c40 100644
--- a/agent_based/fritzbox_smarthome.py
+++ b/agent_based/fritzbox_smarthome.py
@@ -13,255 +13,36 @@
 #
 
 import json
-from dataclasses import dataclass
-from typing import Dict, List
-from time import strftime, gmtime
-from time import time as time_now
+from typing import Dict
 
 from cmk.base.plugins.agent_based.agent_based_api.v1 import (
-    Metric,
     Result,
     Service,
     State,
-    TableRow,
-    check_levels,
-    get_rate,
-    GetRateError,
-    get_value_store,
     register,
 )
 from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
     CheckResult,
     DiscoveryResult,
-    InventoryResult,
     StringTable,
 )
-from cmk.base.plugins.agent_based.utils.temperature import check_temperature
-from cmk.utils.render import physical_precision
 
-
-@dataclass(frozen=True)
-class AvmTemperature:
-    celsius: float
-    offset: float
-
-
-@dataclass(frozen=True)
-class AvmPowerMeter:
-    voltage: float
-    power: float
-    energy: float
-
-
-@dataclass(frozen=True)
-class AvmSimpleOnOff:
-    state: int
-
-
-@dataclass(frozen=True)
-class AvmNextChange:
-    end_period: int
-    temp_change_to: float
-
-
-@dataclass(frozen=True)
-class AvmHkr:
-    temp_current: float
-    temp_target: float
-    temp_economic: float
-    temp_comfort: float
-    lock: int
-    device_lock: int
-    error_code: int
-    battery_low: int
-    next_change: AvmNextChange | None = None
-    # summer_active: int | None = None
-    # window_open_activ: int | None = None
-    # battery: float | None = None
-
-
-@dataclass(frozen=True)
-class AvmSwitch:
-    state: int
-    mode: str
-    lock: int
-    device_lock: int
-
-
-@dataclass(frozen=True)
-class AvmSmartHomeDevice:
-    functions: List[str]
-    identifier: str
-    name: str
-    id: str
-    fbm: int
-    fw_version: str
-    manufacturer: str
-    product_name: str
-    present: int
-    tx_busy: int | None = None
-    temperature: AvmTemperature | None = None
-    hkr: AvmHkr | None = None
-    switch: AvmSwitch | None = None
-    power_meter: AvmPowerMeter | None = None
-    simple_on_off: AvmSimpleOnOff | None = None
-
-
-_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
-_AVM_HKR = 'hkr'
-_AVM_SWITCH = 'switch'
-_AVM_POWER_METER = 'powermeter'
-_AVM_TEMPERATURE = 'temperature'
-_AVM_SIMPLE_ON_OFF = 'simpleonoff'
-_AVM_NEXT_CHANGE = 'nextchange'
-
-__thermostat = [
-    {
-        "identifier": "13979 0878454",
-        "id": "16",
-        "functionbitmask": "320",
-        "fwversion": "05.16",
-        "manufacturer": "AVM",
-        "productname": "Comet DECT",
-        "present": "1",
-        "name": "Temp01",
-        "temperature": {
-            "celsius": "210",
-            "offset": "-10"
-        },
-        "hkr": {
-            "tist": "42",
-            "tsoll": "32",
-            "absenk": "32",
-            "komfort": "38",
-            "lock": "1",
-            "devicelock": "1",
-            "errorcode": "0",
-            "batterylow": "0",
-            "nextchange": {
-                "endperiod": "1704888000",
-                "tchange": "32"
-            }
-        }
-    }
-]
-
-__switch = [
-    {
-        "identifier": "08761 0116372",
-        "id": "17",
-        "functionbitmask": "35712",
-        "fwversion": "04.26",
-        "manufacturer": "AVM",
-        "productname": "FRITZ!DECT 200",
-        "present": "1",
-        "txbusy": "0",
-        "name": "TV-Wohnzimmer",
-        "switch": {
-            "state": "1",
-            "mode": "manuell",
-            "lock": "0",
-            "devicelock": "0"
-        },
-        "simpleonoff": {
-            "state": "1"
-        },
-        "powermeter": {
-            "voltage": "235814",
-            "power": "4220",
-            "energy": "145427"
-        },
-        "temperature": {
-            "celsius": "190",
-            "offset": "0"
-        }
-    }
-]
-
-
-def _get_avm_device_functions_from_fbm(fbm: int) -> List[str]:
-    functions = []
-    if fbm >> 0 & 1:
-        functions.append('HAN-FUN Device')
-    if fbm >> 13 & 1:
-        functions.append('HAN-FUN Unit')
-    if fbm >> 4 & 1:
-        functions.append('Alarm Sensor')
-    if fbm >> 5 & 1:
-        functions.append('Button')
-    if fbm >> 6 & 1:
-        functions.append('Thermostat')
-    if fbm >> 9 & 1:
-        functions.append('Switch')
-    if fbm >> 7 & 1:
-        functions.append('Powermeter')
-    if fbm >> 10 & 1:
-        functions.append('DECT Repeater')
-    if fbm >> 8 & 1:
-        functions.append('Temperature Sensor')
-    if fbm >> 11 & 1:
-        functions.append('Microphone')
-    if fbm >> 17 & 1:
-        functions.append('Light')
-    if fbm >> 2 & 1:
-        functions.append('Light')
-
-    return functions
+from cmk.base.plugins.agent_based.utils.fritzbox_smarthome import (
+    AvmSmartHomeDevice,
+    parse_avm_smarthome_device,
+)
 
 
-def parse_fritzbox_smarthome(string_table: StringTable) -> Dict[str, AvmSmartHomeDevice] | None:
+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
 
-    devices: Dict[str, AvmSmartHomeDevice] = {
-        raw_device['id']: AvmSmartHomeDevice(
-            functions=_get_avm_device_functions_from_fbm(int(raw_device['functionbitmask'])),
-            identifier=str(raw_device['identifier']),
-            name=str(raw_device['name']),
-            id=str(raw_device['id']),
-            fbm=int(raw_device['functionbitmask']),
-            fw_version=str(raw_device['fwversion']),
-            manufacturer=str(raw_device['manufacturer']),
-            product_name=str(raw_device['productname']),
-            present=int(raw_device['present']),
-            tx_busy=int(raw_device['txbusy']) if raw_device.get('txbusy') else None,
-            temperature=AvmTemperature(
-                celsius=float(raw_device[_AVM_TEMPERATURE]['celsius']) / 10.0,
-                offset=float(raw_device[_AVM_TEMPERATURE]['offset']) / 10.0,
-            ) if raw_device.get(_AVM_TEMPERATURE) else None,
-            hkr=AvmHkr(
-                temp_current=float(raw_device[_AVM_HKR]['tist']) / 2.0,
-                temp_target=float(raw_device[_AVM_HKR]['tsoll']) / 2.0,
-                temp_economic=float(raw_device[_AVM_HKR]['absenk']) / 2.0,
-                temp_comfort=float(raw_device[_AVM_HKR]['komfort']) / 2.0,
-                lock=int(raw_device[_AVM_HKR]['lock']),
-                device_lock=int(raw_device[_AVM_HKR]['devicelock']),
-                error_code=int(raw_device[_AVM_HKR]['errorcode']),
-                battery_low=int(raw_device[_AVM_HKR]['batterylow']),
-                next_change=AvmNextChange(
-                    end_period=int(raw_device[_AVM_HKR][_AVM_NEXT_CHANGE]['endperiod']),
-                    temp_change_to=float(raw_device[_AVM_HKR][_AVM_NEXT_CHANGE]['tchange']) / 2.0,
-                ) if raw_device.get(_AVM_NEXT_CHANGE) else None,
-            ) if raw_device.get(_AVM_HKR) else None,
-            switch=AvmSwitch(
-                state=int(raw_device[_AVM_SWITCH]['state']),
-                mode=str(raw_device[_AVM_SWITCH]['mode']),
-                lock=int(raw_device[_AVM_SWITCH]['lock']),
-                device_lock=int(raw_device[_AVM_SWITCH]['devicelock'])
-            ) if raw_device.get(_AVM_SWITCH) else None,
-            power_meter=AvmPowerMeter(
-                voltage=float(raw_device[_AVM_POWER_METER]['voltage']) / 1000,
-                power=float(raw_device[_AVM_POWER_METER]['power']) / 1000,
-                energy=float(raw_device[_AVM_POWER_METER]['energy']),  # / 1000,
-            ) if raw_device.get(_AVM_POWER_METER) else None,
-            simple_on_off=AvmSimpleOnOff(
-                state=int(raw_device[_AVM_SIMPLE_ON_OFF]['state']),
-            ) if raw_device.get(_AVM_SIMPLE_ON_OFF) else None,
-        ) for raw_device in raw_devices
-    }
-    return devices
+    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(
@@ -270,253 +51,87 @@ register.agent_section(
 )
 
 
-def _check_fritzbox_smarthome_hkr(device: AvmSmartHomeDevice, params):
-    _lock = {
-        0: 'no',
-        1: 'yes',
-    }
-    _device_lock = {
-        0: 'no',
-        1: 'yes',
-    }
-    _battery_low = {
-        0: 'no',
-        1: 'yes',
-    }
-
-    _error_codes = {
-        0: 'no error',
-        1: 'No adaptation possible. Is the thermostat correctly mounted on the radiator?',
-        2: 'Valve stroke too short or battery power too low. Open and close the '
-           'valve tappet several times by hand or insert new batteries.',
-        3: 'No valve movement possible. Valve tappet free?',
-        4: 'The installation is currently being prepared.',
-        5: 'The thermostat is in installation mode and can be mounted on the heating valve.',
-        6: 'The thermostat now adapts to the stroke of the heating valve',
-    }
-
-    deviation = device.hkr.temp_current - device.hkr.temp_target
-    _message = f'Temperature current: {device.hkr.temp_current}°C (deviation from target {deviation}°C)'
-    if params['hkr'].get('deviation'):
-        warn, crit = params['hkr']['deviation']
-        if abs(deviation) >= crit:
-            yield Result(state=State.CRIT, notice=_message)
-        elif abs(deviation) >= warn:
-            yield Result(state=State.WARN, notice=_message)
-        else:
-            yield Result(state=State.OK, notice=_message)
-    else:
-        yield Result(state=State.OK, notice=_message)
-
-    if deviation == 0:
-        yield Result(state=State.OK, notice=f'Temperature target: {device.hkr.temp_target}°C')
-    else:
-        yield Result(
-            state=State.OK,
-            summary=f'Target: {device.hkr.temp_target}°C',
-            details=f'Temperature target: {device.hkr.temp_target}°C',
-        )
-    yield Result(state=State.OK, notice=f'Temperature economic: {device.hkr.temp_economic}°C')
-    yield Result(state=State.OK, notice=f'Temperature comfort: {device.hkr.temp_comfort}°C')
-
-    yield Metric(name='temp_current', value=device.hkr.temp_current)
-    yield Metric(name='temp_target', value=device.hkr.temp_target)
-    yield Metric(name='temp_comfort', value=device.hkr.temp_comfort)
-    yield Metric(name='temp_economic', value=device.hkr.temp_economic)
-
-    _message = f'Battery low: {_battery_low.get(device.hkr.battery_low, f"unknown ({device.hkr.battery_low})")}'
-    if device.hkr.battery_low == 0:
-        yield Result(state=State.OK, notice=_message)
-    else:
-        yield Result(state=State(params['hkr'].get('battery_low', 1)), notice=_message)
-
-    _status = _device_lock.get(device.hkr.device_lock, f'unknown ({device.hkr.device_lock})')
-    _message = f'Button lock on the thermostat active: {_status}'
-    yield Result(state=State.OK, notice=_message)
-
-    _status = _lock.get(device.hkr.lock, f'unknown {device.hkr.lock}')
-    _message = f'Deactivate manual access for phone, app or user interface: {_status}'
-    yield Result(state=State.OK, notice=_message)
-
-    if device.hkr.next_change:
-        yield Result(
-            state=State.OK,
-            notice=f'End of period: {strftime(_TIME_FORMAT,gmtime(device.hkr.next_change.end_period))}'
-        )
-        yield Result(state=State.OK, notice=f'Change Temperature to: {device.hkr.next_change.temp_change_to}°C')
-
-    _message = f'Error code: {_error_codes.get(device.hkr.error_code, f"unknown {device.hkr.error_code}")}'
-    if device.hkr.error_code == 0:
-        yield Result(state=State.OK, notice=_message)
-    else:
-        yield Result(state=State(params['hkr'].get('state_on_error', 1)), notice=_message)
-
-
-def _check_fritzbox_smarthome_powermeter(device: AvmSmartHomeDevice, params):
-    try:
-        energy = get_rate(
-            value_store=get_value_store(),
-            key='energy',
-            time=time_now(),
-            value=device.power_meter.energy,
-            raise_overflow=True
-        )
-    except GetRateError as e:
-        yield Result(state=State.OK, notice=str(e))
-    else:
-        yield from check_levels(
-            value=energy,
-            metric_name='energy',
-            label='Consumption current',
-            render_func=lambda x: physical_precision(v=x, precision=3, unit_symbol="Wh")
-        )
-
-    yield Result(
-        state=State.OK,
-        notice=f'Consumption total: {physical_precision(v=device.power_meter.energy, precision=3, unit_symbol="Wh")}'
-    )
-
-    yield from check_levels(
-        value=device.power_meter.voltage,
-        metric_name='voltage',
-        label='Voltage',
-        render_func=lambda x: f'{x}V'
-    )
-    yield from check_levels(
-        value=device.power_meter.power,
-        metric_name='power',
-        label='Power',
-        render_func=lambda x: f'{x}W'
-    )
-
+def discovery_fritzbox_smarthome_single(
+        section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> DiscoveryResult:
+    if isinstance(section, AvmSmartHomeDevice):
+        yield Service()
 
-def _check_fritzbox_smarthome_temperature(device: AvmSmartHomeDevice, params):
-    _params = {
-        'levels': (30, 35),
-        'levels_lower': (-30, -35),
-        'device_levels_handling': 'usr',
-    }
-    yield from check_temperature(
-        reading=device.temperature.celsius,
-        params=None,
-        unique_name=None,
-        value_store=None,
-        dev_levels=None,
-        dev_levels_lower=None,
-        dev_status=None,
-        dev_status_name=None
-    )
-    if device.temperature.offset != 0:
-        _status = device.temperature.celsius + device.temperature.offset * -1
-        _message = f'Temperature measured at the thermostat : {_status}'
-        yield Result(state=State.OK, notice=_message)
-        yield Result(state=State.OK, notice=f'Temperature offset: {device.temperature.offset}')
 
+def 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_simple_on_off(device: AvmSmartHomeDevice, params):
-    _simple_onf_off_state = {
-        0: 'off',
-        1: 'on',
-    }
-    _state = device.simple_on_off.state
-    yield Result(
-        state=State.OK,
-        summary=f'Circuit state: {_simple_onf_off_state.get(_state, f"unknown ({_state}")}'
-    )
 
+def check_fritzbox_smarthome_single(
+        params, section: AvmSmartHomeDevice | Dict[str, AvmSmartHomeDevice]
+) -> CheckResult:
+    if not isinstance(section, AvmSmartHomeDevice):
+        return
 
-def _check_fritzbox_smarthome_switch(device: AvmSmartHomeDevice, params):
-    _switch_state = {
-        0: 'off',
-        1: 'on',
-    }
-    _switch_locked = {
+    _tx_busy = {
         0: 'no',
         1: 'yes',
     }
-    _switch_device_lock = {
-        0: 'no',
-        1: 'yes',
-    }
-    _state = device.switch.state
-    _lock = device.switch.lock
-    _dev_lock = device.switch.device_lock
-    yield Result(state=State.OK, summary=f'Switch state: {_switch_state.get(_state, f"unknown ({_state})")}')
-    yield Result(state=State.OK, notice=f'Switch mode: {device.switch.mode}')
-    yield Result(state=State.OK, notice=f'Switch lock: {_switch_state.get(_lock, f"unknown ({_lock})")}')
-    yield Result(state=State.OK, notice=f'Switch device lock: {_switch_state.get(_dev_lock, f"unknown ({_dev_lock})")}')
-
-
-def discovery_fritzbox_smarthome(section: Dict[str, AvmSmartHomeDevice]) -> DiscoveryResult:
-    for device in section.values():
-        yield Service(item=f'{device.id} {device.name}')
 
-
-def check_fritzbox_smarthome(item, params, section: Dict[str, AvmSmartHomeDevice]) -> CheckResult:
-    try:
-        device = section[item.split(' ')[0]]
-    except KeyError:
-        return
-
-    # move to inventory
     yield Result(
         state=State.OK,
-        notice=f'Device: {device.manufacturer} {device.product_name}, FW: {device.fw_version}'
+        notice=f'Device: {section.manufacturer} {section.product_name}, FW: {section.fw_version}'
     )
 
-    if device.present == 0:
-        yield Result(state=State(params['present']), notice='Device offline')
+    if section.present == 0:
+        yield Result(state=State(params['present']), summary='Device is offline')
         # stop if device is not present
         return
 
-    if device.temperature:
-        yield from _check_fritzbox_smarthome_temperature(device=device, params=params)
-    if device.power_meter:
-        yield from _check_fritzbox_smarthome_powermeter(device=device, params=params)
-    if device.simple_on_off:
-        yield from _check_fritzbox_smarthome_simple_on_off(device=device, params=params)
-    if device.switch:
-        yield from _check_fritzbox_smarthome_switch(device=device, params=params)
-    if device.hkr:
-        yield from _check_fritzbox_smarthome_hkr(device, params)
+    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}]')
 
 
 register.check_plugin(
-    name="fritzbox_smarthome",
-    service_name="SmartDevice %s",
-    discovery_function=discovery_fritzbox_smarthome,
-    check_function=check_fritzbox_smarthome,
-    check_ruleset_name="fritzbox_smarthome",
+    name="fritzbox_smarthome_single",
+    service_name="Smarthome",
+    sections=['fritzbox_smarthome'],
+    discovery_function=discovery_fritzbox_smarthome_single,
+    check_function=check_fritzbox_smarthome_single,
+    check_ruleset_name="fritzbox_smarthome_single",
     check_default_parameters={
         'present': 1,
-        'hkr': {},
         'switch': {},
-        'power_meter': {},
-        'temperature': {},
-        'humidity': {}
     }
 )
 
 
-def inventory_fritzbox_smarthome(section: Dict[str, AvmSmartHomeDevice]) -> InventoryResult:
-    path = ['hardware', 'avm', 'smart_home_devices']
-    for device in section.values():
-        yield TableRow(
-            path=path,
-            key_columns={'id': device.id},
-            inventory_columns={
-                'identifier': device.identifier,
-                'name': device.name,
-                'fw_version': device.fw_version,
-                'manufacturer': device.manufacturer,
-                'product_name': device.product_name,
-                'functions': ', '.join(device.functions)
-            }
-        )
-
-
-register.inventory_plugin(
-    name="inv_fritzbox_smarthome",
+register.check_plugin(
+    name="fritzbox_smarthome_multiple",
+    service_name="Smarthome %s",
     sections=['fritzbox_smarthome'],
-    inventory_function=inventory_fritzbox_smarthome,
+    discovery_function=discovery_fritzbox_smarthome_multiple,
+    check_function=check_fritzbox_smarthome_multiple,
+    check_ruleset_name="fritzbox_smarthome_multiple",
+    check_default_parameters={
+        'present': 1,
+        'switch': {},
+    }
 )
diff --git a/agent_based/fritzbox_smarthome_app_lock.py b/agent_based/fritzbox_smarthome_app_lock.py
new file mode 100644
index 0000000000000000000000000000000000000000..b52314e868c9feb5c542d517f7c46be96c46b179
--- /dev/null
+++ b/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/agent_based/fritzbox_smarthome_battery.py b/agent_based/fritzbox_smarthome_battery.py
new file mode 100644
index 0000000000000000000000000000000000000000..61d597b61e08a8b772b93bc69a952ca0ef4cd051
--- /dev/null
+++ b/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/agent_based/fritzbox_smarthome_device_lock.py b/agent_based/fritzbox_smarthome_device_lock.py
new file mode 100644
index 0000000000000000000000000000000000000000..6c088fd7a7b41578eb49c70221a78d1b59725bd1
--- /dev/null
+++ b/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/agent_based/fritzbox_smarthome_power_meter.py b/agent_based/fritzbox_smarthome_power_meter.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e1c0968b4c9cf0b14806af2775b0695eecd1311
--- /dev/null
+++ b/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/agent_based/fritzbox_smarthome_power_socket.py b/agent_based/fritzbox_smarthome_power_socket.py
new file mode 100644
index 0000000000000000000000000000000000000000..d702cb982da21eb64265129736411316c179fc0d
--- /dev/null
+++ b/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/agent_based/fritzbox_smarthome_switch.py b/agent_based/fritzbox_smarthome_switch.py
new file mode 100644
index 0000000000000000000000000000000000000000..7bfeac10cc0d0b5123851487a22971cc57557b52
--- /dev/null
+++ b/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/agent_based/fritzbox_smarthome_temperature.py b/agent_based/fritzbox_smarthome_temperature.py
new file mode 100644
index 0000000000000000000000000000000000000000..42875b7c17593e59a6768cede99a60a3133aa6b4
--- /dev/null
+++ b/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/agent_based/fritzbox_smarthome_thermostat.py b/agent_based/fritzbox_smarthome_thermostat.py
new file mode 100644
index 0000000000000000000000000000000000000000..f2c748d0922e8fd0147b9fae61b69b2003f88302
--- /dev/null
+++ b/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_target}°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/agent_based/inv_fritzbox_smarthome.py b/agent_based/inv_fritzbox_smarthome.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f77bf5b73d0c1de58c04821adcecfeb1595adf5
--- /dev/null
+++ b/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/agent_based/utils/fritzbox_smarthome.py b/agent_based/utils/fritzbox_smarthome.py
new file mode 100644
index 0000000000000000000000000000000000000000..b6a9137d4fd0e3e03c0ef29975b1cec5f622fc9c
--- /dev/null
+++ b/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/checks/agent_fritzbox_smarthome b/checks/agent_fritzbox_smarthome
index a3918e306d58b14d75a231ecec03561d8f058118..26a785476309e6f6aae7e43c7d47bd8579f0f397 100644
--- a/checks/agent_fritzbox_smarthome
+++ b/checks/agent_fritzbox_smarthome
@@ -25,9 +25,19 @@ def agent_fritzbox_smarthome_arguments(params, hostname, ipaddress):
 
     if (ssl := params.get("ssl")) is not None:
         args.append("--ignore_ssl")
-    if (testing := params.get("ssl")) is not None:
+
+    # 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
 
 
diff --git a/gui/metrics/fritzbox_smarthome.py b/gui/metrics/fritzbox_smarthome.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a8fc1255de4954df05fb55b76f73ae7034464d6
--- /dev/null
+++ b/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/gui/wato/check_parameters/electrical_energy.py b/gui/wato/check_parameters/electrical_energy.py
new file mode 100644
index 0000000000000000000000000000000000000000..737f69ae25d7f62b7be6054d2fd7d101babd8f2f
--- /dev/null
+++ b/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/gui/wato/check_parameters/epower.py b/gui/wato/check_parameters/epower.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5958163afc4378f683fad50d580c97262e4931b
--- /dev/null
+++ b/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/gui/wato/check_parameters/fritzbox_smarthome.py b/gui/wato/check_parameters/fritzbox_smarthome.py
new file mode 100644
index 0000000000000000000000000000000000000000..a102e0e302422008fe6265402ee038d19651bb58
--- /dev/null
+++ b/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/gui/wato/check_parameters/temperature_single.py b/gui/wato/check_parameters/temperature_single.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c9d88797128a4367acbb287ca580b4c5f8bf5f3
--- /dev/null
+++ b/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/gui/wato/check_parameters/voltage_single.py b/gui/wato/check_parameters/voltage_single.py
new file mode 100644
index 0000000000000000000000000000000000000000..7fd6761a10438b84ac57cd446909475e456d6cc8
--- /dev/null
+++ b/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/lib/python3/cmk/special_agents/agent_fritzbox_smarthome.py b/lib/python3/cmk/special_agents/agent_fritzbox_smarthome.py
index c0c2badb2d5b31d09cd5682c6379d0bceff383fa..7e6efef60ba95b88a6ecc9afc58f14dd4e5b877e 100644
--- a/lib/python3/cmk/special_agents/agent_fritzbox_smarthome.py
+++ b/lib/python3/cmk/special_agents/agent_fritzbox_smarthome.py
@@ -6,11 +6,17 @@
 #             changed to return the complete XML response back as json
 # 2023-12-28: added data/option for testing
 
-import sys, traceback, ssl, json
+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
 
 
@@ -34,14 +40,15 @@ def parse_xml_to_json(xml):
 def parse_args():
     parser = argparse.ArgumentParser(description='Check_MK Fritz!Box Smarthome Agent')
     parser.add_argument('host', help='Host name or IP address of your Fritz!Box')
-    parser.add_argument('--username', nargs='?')
-    parser.add_argument('--password', nargs='?')
     parser.add_argument('--debug', action='store_true', default=False,
                         help='Debug mode: let Python exceptions come through')
-
-    parser.add_argument('--port', nargs='?', type=int, default=443)
     parser.add_argument('--ignore_ssl', action='store_true', default=False)
+    parser.add_argument('--no-piggyback', action='store_true', default=False)
+    parser.add_argument('--password', nargs='?')
+    parser.add_argument('--port', nargs='?', type=int, default=443)
+    parser.add_argument('--prefix', nargs='?')
     parser.add_argument('--protocol', nargs='?', choices=['http', 'https'], default='https')
+    parser.add_argument('--username', nargs='?')
     parser.add_argument('--testing', action='store_true', default=False)
     args = parser.parse_args()
 
@@ -99,7 +106,6 @@ def check_fritzbox_smarthome(args):
         raise Exception('Check credentials\n')
 
     # Write section header
-    sys.stdout.write('<<<fritzbox_smarthome:sep(0)>>>\n')
     response = urlopen(
         base_address + '/webservices/homeautoswitch.lua?switchcmd=getdevicelistinfos&sid=%s' % sessionid, context=ctx)
     response_read = response.read()
@@ -112,7 +118,7 @@ def check_fritzbox_smarthome(args):
     devices = []
 
     if args.testing:
-        __switch = {
+        __switch_01 = {
             "identifier": "08761 0116372",
             "id": "99",
             "functionbitmask": "35712",
@@ -141,12 +147,92 @@ def check_fritzbox_smarthome(args):
                 "offset": "0"
             }
         }
-        devices.append(__switch)
+        __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))
 
-    sys.stdout.write(json.dumps(devices))
+    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():
diff --git a/mkp/fritzbox_smarthome-0.8.2-20231230.mkp b/mkp/fritzbox_smarthome-0.8.2-20231230.mkp
new file mode 100644
index 0000000000000000000000000000000000000000..d32b72848621de623bfda86d54f1f3a43018ddfa
Binary files /dev/null and b/mkp/fritzbox_smarthome-0.8.2-20231230.mkp differ
diff --git a/packages/fritzbox_smarthome b/packages/fritzbox_smarthome
index 2c17bf37e7c845b3c83867016fb9dd0c6affb572..740e6c6d6568c93a7e8a2dcb84c3b499081945b8 100644
--- a/packages/fritzbox_smarthome
+++ b/packages/fritzbox_smarthome
@@ -19,18 +19,32 @@
                 '....\n'
                 '\n',
  'download_url': 'https://github.com/MaximilianClemens/checkmk_fritzbox',
- 'files': {'agent_based': ['fritzbox_smarthome.py'],
+ '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/wato/fritzbox_smarthome.py',
-                   'plugins/metrics/fritzbox_smarthome.py',
                    'plugins/views/fritzbox_smarthome.py']},
  'name': 'fritzbox_smarthome',
  'title': 'Fritz!Box SmartHome',
- 'version': '0.7.0-20231228',
+ 'version': '0.8.2-20231230',
  'version.min_required': '2.2.0b1',
  'version.packaged': '2.2.0p14',
  'version.usable_until': None}
diff --git a/web/plugins/wato/agent_fritzbox_smarthome.py b/web/plugins/wato/agent_fritzbox_smarthome.py
index cde48d0a2cf8322cc46b8b2d2ad171e9d6dc2f2f..cc2f86736981940299dbac01368d26e0920ba7d8 100644
--- a/web/plugins/wato/agent_fritzbox_smarthome.py
+++ b/web/plugins/wato/agent_fritzbox_smarthome.py
@@ -56,11 +56,25 @@ def _valuespec_special_agents_fritzbox_smarthome() -> ValueSpec:
              )),
             ('ssl', FixedValue(
                 value=0,
-                totext='Agent will ignore SSL errors',
+                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'),
             )),