diff --git a/README.md b/README.md
index 50601b065f7b566fbf43220cd2409122bc2771d7..e5fe350ff99db0d635c5c281be57512bc40a6807 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[PACKAGE]: ../../raw/master/mkp/cisco_meraki-1.3.2-20240626.mkp "cisco_meraki-1.3.2-20240626.mkp"
+[PACKAGE]: ../../raw/master/mkp/cisco_meraki-1.3.2-20240660.mkp "cisco_meraki-1.3.2-20240660.mkp"
 [SDK]: ../../raw/master/mkp/MerakiSDK-1.46.0-20240516.mkp "MerakiSDK-1.46.0-20240516.mkpp"
 # Cisco Meraki special agent
 
diff --git a/mkp/cisco_meraki-1.3.2-20240660.mkp b/mkp/cisco_meraki-1.3.2-20240660.mkp
new file mode 100644
index 0000000000000000000000000000000000000000..973807245ac69b9b0c0cf90ea75fc8a37f8a4896
Binary files /dev/null and b/mkp/cisco_meraki-1.3.2-20240660.mkp differ
diff --git a/source/agent_based/cisco_meraki_org_device_info.py b/source/agent_based/cisco_meraki_org_device_info.py
index d02a9118eec75ab77e5b2a5e2dd8e2adf06cb1d9..48aacc90a255b41f987aba99fcb7114a4d5fdd26 100644
--- a/source/agent_based/cisco_meraki_org_device_info.py
+++ b/source/agent_based/cisco_meraki_org_device_info.py
@@ -13,7 +13,7 @@ from dataclasses import dataclass
 
 from cmk.base.plugins.agent_based.agent_based_api.v1 import Attributes, register, TableRow, HostLabel
 from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import InventoryResult, StringTable, HostLabelGenerator
-from cmk.base.plugins.agent_based.utils.cisco_meraki import load_json, MerakiAPIData
+from cmk_addons.plugins.meraki.lib.utils import load_json, MerakiAPIData
 
 
 @dataclass(frozen=True)
@@ -33,7 +33,7 @@ class DeviceInfo:
     @classmethod
     def parse(cls, row: MerakiAPIData) -> "DeviceInfo":
         return cls(
-            # Some entries may missing in older API versions
+            # Some entries may be missed in older API versions
             product=str(row.get("productType", "")),
             serial=str(row["serial"]),
             model=str(row["model"]),
diff --git a/source/agent_based/cisco_meraki_org_device_status.py b/source/agent_based/cisco_meraki_org_device_status.py
index 48ebb619e335144ad16e4f231c77d08df1a0a623..56b1c6feff8c3d562e4fd8c93c704a8ac9e0efc5 100644
--- a/source/agent_based/cisco_meraki_org_device_status.py
+++ b/source/agent_based/cisco_meraki_org_device_status.py
@@ -36,11 +36,7 @@ from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
     StringTable,
 )
 
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    check_last_reported_ts,
-    load_json,
-    MerakiAPIData,
-)
+from cmk_addons.plugins.meraki.lib.utils import MerakiAPIData, check_last_reported_ts, load_json
 
 
 @dataclass(frozen=True)
diff --git a/source/agent_based/cisco_meraki_org_licenses_overview.py b/source/agent_based/cisco_meraki_org_licenses_overview.py
index 108c7ca9d61246f8a4a7509ca9796cf16b41424c..095d28ed2753c5e26371fe2f609cfe2c9d65d1e0 100644
--- a/source/agent_based/cisco_meraki_org_licenses_overview.py
+++ b/source/agent_based/cisco_meraki_org_licenses_overview.py
@@ -37,10 +37,7 @@ from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
     StringTable,
     InventoryResult,
 )
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    load_json,
-    MerakiAPIData,
-)
+from cmk_addons.plugins.meraki.lib.utils import MerakiAPIData, add_org_id_name_to_output, load_json
 
 
 @dataclass(frozen=True)
@@ -120,6 +117,13 @@ def check_licenses_overview(
     if (item_data := section.get(params.get('internal_item_name', item))) is None:
         return
 
+    yield from add_org_id_name_to_output(
+        item_data.organisation_id,
+        item_data.organisation_name,
+        params['item_variant'],
+        params.get('dont_show_alias_on_info'),
+    )
+
     yield Result(
         state=State.OK if item_data.status == "OK" else State(params['state_license_not_ok']),
         summary=f"Status: {item_data.status}",
@@ -132,33 +136,10 @@ def check_licenses_overview(
         licensed_devices = sum(item_data.licensed_device_counts.values())
         yield Result(
             state=State.OK,
-            summary=f'Number of licensed devices: {licensed_devices}'
+            summary=f'Licensed devices: {licensed_devices}'
         )
         yield Metric(value=licensed_devices, name='sum_licensed_devices')
 
-    org_id = f'ID: {item_data.organisation_id}'
-    org_name = f'Name: {item_data.organisation_name}'
-    org_id_notice = f'Organisation ID: {item_data.organisation_id}'
-    org_name_notice = f'Organisation name: {item_data.organisation_name}'
-
-    match params['item_variant']:
-        case 'org_id':
-            yield Result(state=State.OK, notice=org_id_notice)
-            if params.get('dont_show_alias_on_info'):
-                yield Result(state=State.OK, notice=org_name_notice)
-            else:
-                yield Result(state=State.OK, summary=org_name)
-        case 'org_name':
-            if params.get('dont_show_alias_on_info'):
-                yield Result(state=State.OK, notice=org_id_notice)
-            else:
-                yield Result(state=State.OK, summary=org_id)
-            yield Result(state=State.OK, notice=org_name_notice)
-
-        case _:
-            yield Result(state=State.OK, notice=org_id_notice)
-            yield Result(state=State.OK, notice=org_name_notice)
-
     for device_type, device_count in sorted(item_data.licensed_device_counts.items(), key=lambda t: t[0], ):
         yield Result(state=State.OK, notice=f"{device_type}: {device_count} licensed devices")
 
@@ -192,11 +173,17 @@ def _check_expiration_date(
 
     else:
         yield from check_levels(
-            age,
+            value=age,
             levels_lower=levels_lower,
             label="Remaining time",
             render_func=render.timespan,
-            metric_name="remaining_time"
+            # metric_name="remaining_time"
+        )
+        # needed as levels don't go the graphing system
+        yield Metric(
+            value=age,
+            name='remaining_time',
+            levels=levels_lower
         )
 
 
@@ -219,7 +206,7 @@ register.check_plugin(
 #
 # inventory license overview
 #
-# ToDo: add senors (MS)
+# ToDo: add senors (MT) -> do the need a license? -> done
 def inventory_licenses_overview(section: Section | None) -> InventoryResult:
     path = ['software', 'applications', 'cisco_meraki', 'licenses']
     for org_id, org_data in section.items():
@@ -233,12 +220,16 @@ def inventory_licenses_overview(section: Section | None) -> InventoryResult:
                 licenses.update({'ms': licenses.get('ms', 0) + device_count})
             elif device_type.lower().startswith('mv'):  # video / camera
                 licenses.update({'mv': licenses.get('mv', 0) + device_count})
+            elif device_type.lower().startswith('mt'):  # sensors
+                licenses.update({'mt': licenses.get('mt', 0) + device_count})
             elif device_type.lower().startswith('mr'):  # access points
                 licenses.update({'mr': licenses.get('mr', 0) + device_count})
             elif device_type.lower().startswith('wireless'):  # merge with access points
                 licenses.update({'mr': licenses.get('mr', 0) + device_count})
+            elif device_type.lower().startswith('sm'):  # systems manager
+                licenses.update({'sm': licenses.get('sm', 0) + device_count})
             else:  # fallback for unknown device type
-                licenses.update({device_type.lower(): device_count})
+                licenses.update({device_type.lower(): licenses.get(device_type.lower(), 0) + device_count})
         licenses.update({'summary': sum(org_data.licensed_device_counts.values())})
 
         yield TableRow(
diff --git a/source/agent_based/cisco_meraki_org_sensor_readings.py_ b/source/agent_based/cisco_meraki_org_sensor_readings.py_
new file mode 100644
index 0000000000000000000000000000000000000000..10e9d86ee5f997c98304d789cfeec045e3a2cf35
--- /dev/null
+++ b/source/agent_based/cisco_meraki_org_sensor_readings.py_
@@ -0,0 +1,240 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Copyright (C) 2022 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.
+
+# enhancements by thl-cmk[at]outlook[dot]com, https://thl-cmk.hopto.org
+# 2023-11-10: removed ts check/sort, we get always only the last reading, so need to sort by last reported
+#             added ability to handle temperature, humidity and battery at the same time, not sure if there
+#             is a need to add some index (multiple temperature reading for example)
+#             added battery/humidity check
+#             changed section from Sequence to Mapping by metric
+
+
+from dataclasses import dataclass
+from datetime import datetime
+from collections.abc import Sequence, Mapping
+
+from cmk.base.plugins.agent_based.agent_based_api.v1 import get_value_store, register, Service, render
+from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import CheckResult, DiscoveryResult, StringTable
+from cmk.base.plugins.agent_based.utils.temperature import check_temperature, TempParamType, check_levels
+from cmk_addons.plugins.meraki.lib.utils import check_last_reported_ts, load_json, MerakiAPIData
+
+
+@dataclass(frozen=True)
+class SensorReadings:
+    metric: str
+    unit: str
+    last_reported: datetime | None = None
+    reading: float | None = None
+
+    @classmethod
+    def parse(cls, row: MerakiAPIData) -> Sequence["SensorReadings"] | None:
+        if not isinstance(raw_readings := row.get("readings"), list):
+            return None
+
+        # not needed, we have only the last reading.
+        # if not (
+        #     readings_by_datetime := {
+        #         reading_datetime: raw_reading
+        #         for raw_reading in raw_readings
+        #         if (reading_datetime := cls._parse_ts(raw_reading["ts"])) is not None
+        #     }
+        # ):
+        #     return None
+        # last_reported, readings = sorted(readings_by_datetime.items(), key=lambda t: t[0], reverse=True)[0]
+
+        parsed_readings = {}
+        for raw_reading in raw_readings:
+            sensor_type = raw_reading['metric']
+            match sensor_type:
+                case 'battery':
+                    sensor_unit = 'percentage'
+                case 'humidity':
+                    sensor_unit = 'relativePercentage'
+                case 'temperature':
+                    sensor_unit = 'celsius'
+                case _:
+                    return None
+
+            parsed_readings.update({sensor_type: cls(
+                last_reported=cls._parse_ts(raw_reading["ts"]),
+                metric=sensor_type,
+                unit=sensor_unit,
+                reading=cls._parse_reading(
+                    reading=raw_reading,
+                    sensor_type=sensor_type,
+                    sensor_unit=sensor_unit,
+                ),
+            )})
+
+        return parsed_readings
+
+    @staticmethod
+    def _parse_ts(raw_ts: str) -> datetime | None:
+        try:
+            return datetime.strptime(raw_ts, "%Y-%m-%dT%H:%M:%SZ")
+        except ValueError:
+            return None
+
+    @staticmethod
+    def _parse_reading(reading: MerakiAPIData, sensor_type: str, sensor_unit: str) -> float | None:
+        try:
+            sensor_data = reading[sensor_type]
+        except KeyError:
+            return None
+
+        if not isinstance(sensor_data, dict):
+            return None
+
+        try:
+            return float(sensor_data[sensor_unit])
+        except (KeyError, ValueError):
+            return None
+
+
+def parse_sensor_readings(string_table: StringTable) -> SensorReadings | None:
+    return (
+        SensorReadings.parse(loaded_json[0]) if (loaded_json := load_json(string_table)) else None
+    )
+
+
+register.agent_section(
+    name="cisco_meraki_org_sensor_readings",
+    parse_function=parse_sensor_readings,
+)
+
+
+def discover_sensor_temperature(
+        section: Mapping[SensorReadings] | None,
+) -> DiscoveryResult:
+    if 'temperature' in section.keys():
+        yield Service(item='Temperature')
+
+
+def check_sensor_temperature(
+        item: str,
+        params: TempParamType,
+        section: Mapping[SensorReadings] | None,
+) -> CheckResult:
+    try:
+        reading = section[item.lower()]
+    except KeyError:
+        return None
+
+    yield from check_temperature(
+        reading=reading.reading,
+        params=params,
+        unique_name=item,
+        value_store=get_value_store(),
+    )
+
+    if reading.last_reported is not None:
+        yield from check_last_reported_ts(
+            last_reported_ts=reading.last_reported.timestamp(),
+            as_metric=False,
+        )
+
+
+register.check_plugin(
+    name="cisco_meraki_org_sensor_temperature",
+    sections=["cisco_meraki_org_sensor_readings"],
+    service_name="Sensor %s",
+    discovery_function=discover_sensor_temperature,
+    check_function=check_sensor_temperature,
+    check_ruleset_name="temperature",
+    check_default_parameters={
+        # "levels": (50.0, 60.0),
+    },
+)
+
+
+def discover_sensor_humidity(
+        section: Mapping[SensorReadings] | None,
+) -> DiscoveryResult:
+    if 'humidity' in section.keys():
+        yield Service(item='Humidity')
+
+
+def check_sensor_humidity(
+        item: str,
+        params: TempParamType,
+        section: Mapping[SensorReadings] | None,
+) -> CheckResult:
+    try:
+        reading = section[item.lower()]
+    except KeyError:
+        return None
+
+    yield from check_levels(
+        value=reading.reading,
+        label="Relative Humidity",
+        levels_upper=params.get('levels_upper', None),
+        metric_name='humidity',
+        render_func=render.percent
+    )
+
+    if reading.last_reported is not None:
+        yield from check_last_reported_ts(
+            last_reported_ts=reading.last_reported.timestamp(),
+            as_metric=False,
+        )
+
+
+register.check_plugin(
+    name="cisco_meraki_org_sensor_humidity",
+    sections=["cisco_meraki_org_sensor_readings"],
+    service_name="Sensor %s",
+    discovery_function=discover_sensor_humidity,
+    check_function=check_sensor_humidity,
+    check_ruleset_name="humidity",
+    check_default_parameters={
+        # "levels": (50.0, 60.0),
+    },
+)
+
+
+def discover_sensor_battery(
+        section: Mapping[SensorReadings] | None,
+) -> DiscoveryResult:
+    if 'battery' in section.keys():
+        yield Service(item='Battery')
+
+
+def check_sensor_battery(
+        item: str,
+        params: TempParamType,
+        section: Mapping[SensorReadings] | None,
+) -> CheckResult:
+    try:
+        reading = section[item.lower()]
+    except KeyError:
+        return None
+
+    yield from check_levels(
+        value=reading.reading,
+        label='Battery level',
+        levels_upper=params.get('levels_upper', None),
+        metric_name='battery',
+        render_func=render.percent
+    )
+
+    if reading.last_reported is not None:
+        yield from check_last_reported_ts(
+            last_reported_ts=reading.last_reported.timestamp(),
+            as_metric=False,
+        )
+
+
+register.check_plugin(
+    name="cisco_meraki_org_sensor_battery",
+    sections=["cisco_meraki_org_sensor_readings"],
+    service_name="Sensor %s",
+    discovery_function=discover_sensor_battery,
+    check_function=check_sensor_battery,
+    check_ruleset_name="battery",
+    check_default_parameters={
+        # "levels": (50.0, 60.0),
+    },
+)
diff --git a/source/checks/agent_cisco_meraki b/source/checks/agent_cisco_meraki
deleted file mode 100644
index 74230b874006e58d5f574213ebafe09fbab2a5e0..0000000000000000000000000000000000000000
--- a/source/checks/agent_cisco_meraki
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-# Copyright (C) 2022 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.
-
-# enhancements by thl-cmk[at]outlook[dot]com, https://thl-cmk.hopto.org
-# - added host_suffix_prefix option
-# - added no-cache option
-# 2023-11-18: changed from section to excluded_sections
-# 2023-11-22: replaced host_suffix_prefix option by org_id_as_prefix
-# 2024-06-23: added cache time per section -> not nice but should work.
-
-#
-# needs to be re implemented for CMK 2.3.X
-# https://github.com/Checkmk/checkmk/commit/c12cca9fe631d935ed5f239c23288ea856869e6e
-#
-
-from collections.abc import Mapping, Sequence
-from typing import Any
-
-
-def agent_cisco_meraki_arguments(
-    params: Mapping[str, Any],
-    hostname: str,
-    ipaddress: str | None,
-) -> Sequence[object]:
-    args = [
-        hostname,
-        passwordstore_get_cmdline("%s", params["api_key"]),
-    ]
-
-    if (proxy := params.get("proxy")) is not None:
-        args.extend(
-            [
-                "--proxy",
-                get_http_proxy(proxy).serialize(),
-            ]
-        )
-
-    if orgs := params.get("orgs"):
-        args.extend(["--orgs"] + orgs)
-
-    if params.get("no_cache"):
-        args.append('--no-cache')
-
-    if params.get("org_id_as_prefix"):
-        args.append('--org-id-as-prefix')
-
-    if excluded_sections := params.get("excluded_sections"):
-        args.extend(["--excluded-sections"] + excluded_sections)
-
-    if cache_per_section := params.get("cache_per_section"):
-        args.extend(["--cache-per-section"] + list(cache_per_section))
-
-    return args
-
-
-special_agent_info["cisco_meraki"] = agent_cisco_meraki_arguments
diff --git a/source/agent_based/cisco_meraki_org_appliance_performance.py b/source/cmk_addons_plugins/meraki/agent_based/appliance_performance.py
similarity index 65%
rename from source/agent_based/cisco_meraki_org_appliance_performance.py
rename to source/cmk_addons_plugins/meraki/agent_based/appliance_performance.py
index 1ce4e1d78f52f290058328542238e52f94e444e9..fce7cf5e09bd0acc4acffd603e958bf9f22c5c82 100644
--- a/source/agent_based/cisco_meraki_org_appliance_performance.py
+++ b/source/cmk_addons_plugins/meraki/agent_based/appliance_performance.py
@@ -5,33 +5,35 @@
 #
 # Author: thl-cmk[at]outlook[dot]com
 # URL   : https://thl-cmk.hopto.org
-# Date  : 2023-11-04
+# Date  : 2024-06-20
 # File  : cisco_meraki_appliance_performance.py (check plugin)
 
+# 2024-06-29: refactored for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki_org_appliance_performance.py in to appliance_performance.py
 
-from _collections_abc import Mapping
+from collections.abc import Mapping
 
-from cmk.base.plugins.agent_based.agent_based_api.v1 import (
-    register,
-    check_levels,
-    Service,
-    render,
-)
-from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
+from cmk.agent_based.v2 import (
+    AgentSection,
+    CheckPlugin,
     CheckResult,
     DiscoveryResult,
+    Service,
     StringTable,
+    check_levels,
+    render,
 )
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    load_json,
-)
+
+from cmk_addons.plugins.meraki.lib.utils import load_json
+
 
 # sample agent output
 # {"perfScore": 1}
 # sample string_table
 # [['[{"perfScore": 0}]']]
 
-def parse_appliance_performance(string_table: StringTable) -> int:
+
+def parse_appliance_performance(string_table: StringTable) -> int | None:
     json_data = load_json(string_table)
     if (json_data := json_data[0]) is None:
         return
@@ -41,7 +43,7 @@ def parse_appliance_performance(string_table: StringTable) -> int:
         return int(perfscore)
 
 
-register.agent_section(
+agent_section_meraki_org_appliance_performance = AgentSection(
     name="cisco_meraki_org_appliance_performance",
     parse_function=parse_appliance_performance,
 )
@@ -55,20 +57,20 @@ def check_appliance_performance(params: Mapping[str, any], section: int) -> Chec
     yield from check_levels(
         value=section,
         label='Utilization',
-        levels_upper=params.get('levels_upper'),
+        levels_upper=params['levels_upper'],
         render_func=render.percent,
         metric_name='utilization',
         boundaries=(0, 100),
     )
 
 
-register.check_plugin(
-    name='cisco_meraki_org_appliance_performance',
-    service_name='Utilization',
-    discovery_function=discover_appliance_performance,
+check_plugin_meraki_org_appliance_performance = CheckPlugin(
+    name="cisco_meraki_org_appliance_performance",
+    service_name="Utilization",
     check_function=check_appliance_performance,
+    discovery_function=discover_appliance_performance,
+    check_ruleset_name="cisco_meraki_org_appliance_performance",
     check_default_parameters={
-        'levels_upper': (60, 80),
+        'levels_upper': ('fixed', (60, 80)),
     },
-    check_ruleset_name='cisco_meraki_org_appliance_performance',
 )
diff --git a/source/agent_based/cisco_meraki_org_appliance_uplinks.py b/source/cmk_addons_plugins/meraki/agent_based/appliance_uplinks.py
similarity index 86%
rename from source/agent_based/cisco_meraki_org_appliance_uplinks.py
rename to source/cmk_addons_plugins/meraki/agent_based/appliance_uplinks.py
index 5bfe043668641ede214c5c2394bd2636dcbf8d98..6879d6c272ba4371dbc038ea208aa6620be00813 100644
--- a/source/agent_based/cisco_meraki_org_appliance_uplinks.py
+++ b/source/cmk_addons_plugins/meraki/agent_based/appliance_uplinks.py
@@ -15,28 +15,29 @@
 #             moved parse function to the dataclasses
 # 2024-05-19: reworked appliance uplinks usage
 # 2024-04-24: fixed, we can have no traffic if uplinc is not connected
+# 2024-06-29: refactored for CMK 2.3
+#             changed service name from "Appliance Uplink" to "Uplink"
+#             fixed render function for bandwidth -> uses now render.networkbandwidth
+# 2024-06-30: renamed from cisco_meraki_org_appliance_uplinks.py in to appliance_uplinks.py
 
+from collections.abc import Mapping
 from dataclasses import dataclass
 from datetime import datetime
-from _collections_abc import Mapping
 
-from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+from cmk.agent_based.v2 import (
+    AgentSection,
+    CheckPlugin,
+    CheckResult,
+    DiscoveryResult,
     Result,
     Service,
     State,
+    StringTable,
     check_levels,
-    register,
     render,
 )
-from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
-    CheckResult,
-    DiscoveryResult,
-    StringTable,
-)
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    get_int,  # type: ignore[import]
-    load_json,
-)
+
+from cmk_addons.plugins.meraki.lib.utils import get_int, load_json
 
 # sample string_table
 __appliance_uplinks = [
@@ -150,7 +151,7 @@ def parse_appliance_uplinks(string_table: StringTable) -> Appliance | None:
     return Appliance.parse(json_data[0])
 
 
-register.agent_section(
+agent_section_cisco_meraki_org_appliance_uplinks = AgentSection(
     name="cisco_meraki_org_appliance_uplinks",
     parse_function=parse_appliance_uplinks,
 )
@@ -164,21 +165,23 @@ def discover_appliance_uplinks(section: Appliance) -> DiscoveryResult:
 _STATUS_MAP = {
     "active": 0,
     "failed": 2,
-    "not connected": 1,
+    "not_connected": 1,
     "ready": 0,
 }
-
 _TIMESPAN = 60
 
 
+def render_network_bandwidth_bits(value: int) -> str:
+    return render.networkbandwidth(value/8)
+
+
 def check_appliance_uplinks(item: str, params: Mapping[str, any], section: Appliance) -> CheckResult:
-    try:
-        uplink: ApplianceUplink = section.uplinks[item]
-    except KeyError:
+    if (uplink := section.uplinks.get(item)) is None:
         return None
 
     if params.get('status_map'):
         _STATUS_MAP.update(params['status_map'])
+    _STATUS_MAP['not connected'] = _STATUS_MAP['not_connected']  # can not use 'nor connected' in params anymore :-(
 
     yield Result(state=State(_STATUS_MAP.get(uplink.status, 3)), summary=f'Status: {uplink.status}')
     if uplink.ip:
@@ -187,15 +190,14 @@ def check_appliance_uplinks(item: str, params: Mapping[str, any], section: Appli
         yield Result(state=State.OK, summary=f'Public IP: {uplink.public_ip}')
     yield Result(state=State.OK, notice=f'Network: {section.network_name}')
 
-    if uplink.status in ['active']:  # we can only have traffic, if uplinc is connected
+    if params.get('show_traffic') and uplink.status in ['active']:  # we can only have traffic, if uplink is connected
         if uplink.received:  # and params.get('show_traffic'):
             value = uplink.received * 8 / _TIMESPAN  # Bits / Timespan
             yield from check_levels(
                 value=value,  # Bits
                 label='In',
                 metric_name='if_in_bps',
-                render_func=lambda v: render.networkbandwidth(v/8),  # Bytes
-                # notice_only=True,
+                render_func=render_network_bandwidth_bits, # Bytes
             )
 
         if uplink.sent:  # and params.get('show_traffic'):
@@ -204,8 +206,7 @@ def check_appliance_uplinks(item: str, params: Mapping[str, any], section: Appli
                 value=value,  # Bits
                 label='Out',
                 metric_name='if_out_bps',
-                render_func=lambda v: render.networkbandwidth(v/8),  # Bytes
-                # notice_only=True,
+                render_func=render_network_bandwidth_bits, # Bytes
             )
 
     # not needed, will show in device status (?)
@@ -225,9 +226,9 @@ def check_appliance_uplinks(item: str, params: Mapping[str, any], section: Appli
         yield Result(state=State.OK, notice=f'Secondary DNS: {uplink.secondary_dns}')
 
 
-register.check_plugin(
+check_plugin_cisco_meraki_org_appliance_uplinks = CheckPlugin(
     name='cisco_meraki_org_appliance_uplinks',
-    service_name='Appliance Uplink %s',
+    service_name='Uplink %s',
     discovery_function=discover_appliance_uplinks,
     check_function=check_appliance_uplinks,
     check_default_parameters={},
diff --git a/source/agent_based/cisco_meraki_org_appliance_vpns.py b/source/cmk_addons_plugins/meraki/agent_based/appliance_vpns.py
similarity index 91%
rename from source/agent_based/cisco_meraki_org_appliance_vpns.py
rename to source/cmk_addons_plugins/meraki/agent_based/appliance_vpns.py
index 25b76d177ad5e3e4413a09f7c24f06bd6deb18ac..e0856fee41dbc023ad918fa851292fc64dfe3f0a 100644
--- a/source/agent_based/cisco_meraki_org_appliance_vpns.py
+++ b/source/cmk_addons_plugins/meraki/agent_based/appliance_vpns.py
@@ -10,25 +10,26 @@
 
 # 2024-04-27: made data parsing more robust
 # 2024-05-15: moved parse function to data classes
+# 2024-06-29: refactored for CMK 2.3
+#             changed service name from "Appliance VPN" to "VPN peer"
+# 2024-06-30: renamed from cisco_meraki_org_appliance_vpns.py in to appliance_vpns.py
 
 from abc import abstractmethod
+from collections.abc import Mapping, Sequence
 from dataclasses import dataclass
-from _collections_abc import Mapping, Sequence
 
-from cmk.base.plugins.agent_based.agent_based_api.v1 import (
-    register,
+from cmk.agent_based.v2 import (
+    AgentSection,
+    CheckPlugin,
+    CheckResult,
+    DiscoveryResult,
     Result,
     Service,
     State,
-)
-from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
-    CheckResult,
-    DiscoveryResult,
     StringTable,
 )
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    load_json,
-)
+
+from cmk_addons.plugins.meraki.lib.utils import load_json
 
 # sample string_table
 __appliance_vpn_statuses = [
@@ -163,7 +164,7 @@ def parse_appliance_vpns(string_table: StringTable) -> Mapping[str, ApplianceVPN
     return meraki_peers
 
 
-register.agent_section(
+agent_section_cisco_meraki_org_appliance_vpns = AgentSection(
     name="cisco_meraki_org_appliance_vpns",
     parse_function=parse_appliance_vpns,
 )
@@ -175,9 +176,7 @@ def discover_appliance_vpns(section: Mapping[str, ApplianceVPNPeer]) -> Discover
 
 
 def check_appliance_vpns(item: str, params: Mapping[str, any], section: Mapping[str, ApplianceVPNPeer]) -> CheckResult:
-    try:
-        peer: ApplianceVPNPeer = section[item]
-    except KeyError:
+    if (peer := section.get(item)) is None:
         return None
 
     if peer.reachability is not None and peer.reachability.lower() in ['reachable']:
@@ -198,9 +197,9 @@ def check_appliance_vpns(item: str, params: Mapping[str, any], section: Mapping[
         yield Result(state=State.OK, notice=f'name: {uplink.interface}, public IP: {uplink.public_ip}')
 
 
-register.check_plugin(
+check_plugin_cisco_meraki_org_appliance_vpns = CheckPlugin(
     name='cisco_meraki_org_appliance_vpns',
-    service_name='Appliance VPN %s',
+    service_name='VPN peer %s',
     discovery_function=discover_appliance_vpns,
     check_function=check_appliance_vpns,
     check_default_parameters={},
diff --git a/source/agent_based/cisco_meraki_org_cellular_uplinks.py b/source/cmk_addons_plugins/meraki/agent_based/cellular_uplinks.py
similarity index 59%
rename from source/agent_based/cisco_meraki_org_cellular_uplinks.py
rename to source/cmk_addons_plugins/meraki/agent_based/cellular_uplinks.py
index 0261165027847b69bbcea9285418381b5927d8c4..06c149e54fad1d1dd7dbcca76e1dd90ceb8e4996 100644
--- a/source/agent_based/cisco_meraki_org_cellular_uplinks.py
+++ b/source/cmk_addons_plugins/meraki/agent_based/cellular_uplinks.py
@@ -9,30 +9,63 @@
 # File  : cisco_meraki_org_cellular_uplinks.py (check plugin)
 
 # 2024-04-27: made data parsing more robust
+# 2024-06-29: refactored for CMK 2.3
+#             moved parse functions to class methods
+#             changed service name from "Cellular uplink" to "Uplink"
+# 2024-06-30: renamed from cisco_meraki_org_cellular_uplinks.py in to cellular_uplinks.py
 
-
-from _collections_abc import Mapping
+from collections.abc import Mapping
 from dataclasses import dataclass
 from datetime import datetime
 
-from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+from cmk.agent_based.v2 import (
+    AgentSection,
+    CheckPlugin,
+    CheckResult,
+    DiscoveryResult,
     Metric,
     Result,
     Service,
     State,
-    register,
-    render,
-)
-from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
-    CheckResult,
-    DiscoveryResult,
     StringTable,
+    render,
 )
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    get_int,  # type: ignore[import]
-    load_json,
-)
 
+from cmk_addons.plugins.meraki.lib.utils import get_int, load_json
+
+__cellular_uplinks = [
+    {
+        "highAvailability": {
+            "enabled": False,
+            "role": "primary"
+        },
+        "lastReportedAt": "2023-11-13T19:52:06Z",
+        "model": "MG41",
+        "networkId": "L_575897802350012343",
+        "serial": "QQQQ-XXXX-ZZZZ",
+        "uplinks": [
+            {
+                "apn": "apn.name",
+                "connectionType": "lte",
+                "dns1": None,
+                "dns2": None,
+                "gateway": None,
+                "iccid": "89492027206012345518",
+                "interface": "cellular",
+                "ip": None,
+                "model": "integrated",
+                "provider": "provider.name",
+                "publicIp": "2.3.4.5",
+                "signalStat": {
+                    "rsrp": "-111",
+                    "rsrq": "-8"
+                },
+                "signalType": None,
+                "status": "active"
+            }
+        ]
+    }
+]
 
 _LAST_REPORTED_AT = "%Y-%m-%dT%H:%M:%SZ"
 
@@ -57,12 +90,41 @@ class CellularUplink:
     signal_type: str | None
     status: str | None
 
+    @classmethod
+    def parse(cls, uplink: Mapping[str, object]):
+        return cls(
+            apn=str(uplink['apn']) if uplink.get('apn') is not None else None,
+            connection_type=str(uplink['connectionType']) if uplink.get('connectionType') is not None else None,
+            dns1=str(uplink['dns1']) if uplink.get('dns1') is not None else None,
+            dns2=str(uplink['dns2']) if uplink.get('dns2') is not None else None,
+            gateway=str(uplink['gateway']) if uplink.get('gateway') is not None else None,
+            iccid=str(uplink['iccid']) if uplink.get('iccid') is not None else None,
+            interface=str(uplink['interface']) if uplink.get('interface') is not None else None,
+            ip=str(uplink['ip']) if uplink.get('ip') is not None else None,
+            model=str(uplink['model']) if uplink.get('model') is not None else None,
+            provider=str(uplink['provider']) if uplink.get('provider') is not None else None,
+            public_ip=str(uplink['publicIp']) if uplink.get('publicIp') is not None else None,
+            signal_type=str(uplink['signalType']) if uplink.get('signalType') is not None else None,
+            status=str(uplink['status']) if uplink.get('status') is not None else None,
+            rsrp=get_int(uplink.get('signalStat', {}).get('rsrp')),
+            rsrq=get_int(uplink.get('signalStat', {}).get('rsrq')),
+            received=get_int(uplink.get('received')),
+            sent=get_int(uplink.get('sent')),
+        )
+
 
 @dataclass(frozen=True)
 class CellularUplinkHA:
     enabled: bool | None
     role: str | None
 
+    @classmethod
+    def parse(cls, high_availability: Mapping[str, object]):
+        return cls(
+            enabled=bool(high_availability['enabled']) if high_availability.get('enabled') is not None else None,
+            role=str(high_availability['role']) if high_availability.get('enabled') is not None else None,
+        )
+
 
 @dataclass(frozen=True)
 class CellularGateway:
@@ -73,82 +135,30 @@ class CellularGateway:
     serial: str | None
     uplinks: Mapping[str, CellularUplink] | None
 
-
-__cellular_uplinks = [
-    {
-        "highAvailability": {
-            "enabled": False,
-            "role": "primary"
-        },
-        "lastReportedAt": "2023-11-13T19:52:06Z",
-        "model": "MG41",
-        "networkId": "L_575897802350012343",
-        "serial": "QQQQ-XXXX-ZZZZ",
-        "uplinks": [
-            {
-                "apn": "apn.name",
-                "connectionType": "lte",
-                "dns1": None,
-                "dns2": None,
-                "gateway": None,
-                "iccid": "89492027206012345518",
-                "interface": "cellular",
-                "ip": None,
-                "model": "integrated",
-                "provider": "provider.name",
-                "publicIp": "2.3.4.5",
-                "signalStat": {
-                    "rsrp": "-111",
-                    "rsrq": "-8"
-                },
-                "signalType": None,
-                "status": "active"
-            }
-        ]
-    }
-]
+    @classmethod
+    def parse(cls, cellular_gateway):
+        return cls(
+            serial=str(cellular_gateway['serial']) if cellular_gateway.get('serial') is not None else None,
+            model=str(cellular_gateway['model']) if cellular_gateway.get('model') is not None else None,
+            last_reported_at=datetime.strptime(
+                cellular_gateway['lastReportedAt'], _LAST_REPORTED_AT) if cellular_gateway.get(
+                'lastReportedAt'
+            ) is not None else None,
+            # network_name=str(json_data['networkName']) if json_data.get('networkName') is not None else None,
+            high_availability=CellularUplinkHA.parse(cellular_gateway.get('highAvailability', {})),
+            uplinks={
+                uplink['interface']: CellularUplink.parse(uplink) for uplink in cellular_gateway.get('uplinks', [])
+            },
+        )
 
 
 def parse_cellular_uplinks(string_table: StringTable) -> CellularGateway | None:
     json_data = load_json(string_table)
     json_data = json_data[0]
-    return CellularGateway(
-        serial=str(json_data['serial']) if json_data.get('serial') is not None else None,
-        model=str(json_data['model']) if json_data.get('model') is not None else None,
-        last_reported_at=datetime.strptime(json_data['lastReportedAt'], _LAST_REPORTED_AT) if json_data.get(
-            'lastReportedAt') is not None else None,
-        # network_name=str(json_data['networkName']) if json_data.get('networkName') is not None else None,
-        high_availability=CellularUplinkHA(
-            enabled=bool(json_data['highAvailability']['enabled']) if json_data.get('highAvailability', {}).get(
-                'enabled') is not None else None,
-            role=str(json_data['highAvailability']['role']) if json_data.get('highAvailability', {}).get(
-                'enabled') is not None else None,
-        ),
-        uplinks={
-            uplink['interface']: CellularUplink(
-                apn=str(uplink['apn']) if uplink.get('apn') is not None else None,
-                connection_type=str(uplink['connectionType']) if uplink.get('connectionType') is not None else None,
-                dns1=str(uplink['dns1']) if uplink.get('dns1') is not None else None,
-                dns2=str(uplink['dns2']) if uplink.get('dns2') is not None else None,
-                gateway=str(uplink['gateway']) if uplink.get('gateway') is not None else None,
-                iccid=str(uplink['iccid']) if uplink.get('iccid') is not None else None,
-                interface=str(uplink['interface']) if uplink.get('interface') is not None else None,
-                ip=str(uplink['ip']) if uplink.get('ip') is not None else None,
-                model=str(uplink['model']) if uplink.get('model') is not None else None,
-                provider=str(uplink['provider']) if uplink.get('provider') is not None else None,
-                public_ip=str(uplink['publicIp']) if uplink.get('publicIp') is not None else None,
-                signal_type=str(uplink['signalType']) if uplink.get('signalType') is not None else None,
-                status=str(uplink['status']) if uplink.get('status') is not None else None,
-                rsrp=get_int(uplink.get('signalStat', {}).get('rsrp')),
-                rsrq=get_int(uplink.get('signalStat', {}).get('rsrq')),
-                received=get_int(uplink.get('received')),
-                sent=get_int(uplink.get('sent')),
-            ) for uplink in json_data.get('uplinks', [])
-        },
-    )
+    return CellularGateway.parse(json_data)
 
 
-register.agent_section(
+agent_section_cisco_meraki_org_cellular_uplinks = AgentSection(
     name="cisco_meraki_org_cellular_uplinks",
     parse_function=parse_cellular_uplinks,
 )
@@ -160,10 +170,8 @@ def discover_cellular_uplinks(section: CellularGateway) -> DiscoveryResult:
 
 
 def check_cellular_uplinks(item: str, params: Mapping[str, any], section: CellularGateway) -> CheckResult:
-    try:
-        uplink: CellularUplink = section.uplinks[item]
-    except KeyError:
-        return None
+    if (uplink := section.uplinks.get(item)) is None:
+        return
 
     if uplink.status not in ['active']:
         yield Result(state=State(params.get('status_not_active', 1)), summary=f'Status: {uplink.status}')
@@ -206,9 +214,9 @@ def check_cellular_uplinks(item: str, params: Mapping[str, any], section: Cellul
     yield Result(state=State.OK, notice=f'DNS 2: {uplink.dns2}')
 
 
-register.check_plugin(
+check_plugin_cisco_meraki_org_cellular_uplinks = CheckPlugin(
     name='cisco_meraki_org_cellular_uplinks',
-    service_name='Cellular Uplink %s',
+    service_name='Uplink %s',
     discovery_function=discover_cellular_uplinks,
     check_function=check_cellular_uplinks,
     check_default_parameters={},
diff --git a/source/agent_based/cisco_meraki_org_device_uplinks.py b/source/cmk_addons_plugins/meraki/agent_based/device_uplinks.py
similarity index 77%
rename from source/agent_based/cisco_meraki_org_device_uplinks.py
rename to source/cmk_addons_plugins/meraki/agent_based/device_uplinks.py
index 47b181cee73f8f94afbed5b40a02c2440d67370b..26ccfc8e565bfee098c0d1545ed38dc60f620d25 100644
--- a/source/agent_based/cisco_meraki_org_device_uplinks.py
+++ b/source/cmk_addons_plugins/meraki/agent_based/device_uplinks.py
@@ -11,12 +11,21 @@
 # inventory of cisco Meraki uplinks
 
 # 2024-04-27: made data parsing more robust
+# 2024-06-29: refactored for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki_org_device_uplinks.py in to device_uplinks.py
+
 
 from collections.abc import Sequence
 
-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, StringTable
-from cmk.base.plugins.agent_based.utils.cisco_meraki import load_json
+from cmk.agent_based.v2 import (
+    AgentSection,
+    InventoryPlugin,
+    InventoryResult,
+    StringTable,
+    TableRow,
+)
+
+from cmk_addons.plugins.meraki.lib.utils import load_json
 
 __uplinks = [
     {
@@ -40,7 +49,7 @@ def parse_device_uplinks(string_table: StringTable) -> Sequence | None:
     return loaded_json[0]['uplinks'] if (loaded_json := load_json(string_table)) else None
 
 
-register.agent_section(
+agent_section_cisco_meraki_org_device_uplinks_info = AgentSection(
     name="cisco_meraki_org_device_uplinks_info",
     parse_function=parse_device_uplinks,
 )
@@ -58,7 +67,9 @@ def inventory_device_uplinks(section: Sequence | None) -> InventoryResult:
                 **({"address": str(address['address'])} if address.get('address') is not None else {}),
             }
             inventory_columns = {
-                **({"assignment_mode": str(address['assignmentMode'])} if address.get('assignmentMode') is not None else {}),
+                **({"assignment_mode": str(
+                    address['assignmentMode']
+                )} if address.get('assignmentMode') is not None else {}),
                 **({"gateway": str(address['gateway'])} if address.get('gateway') is not None else {}),
                 **({"public_address": str(address['public']['address'])} if address.get('public', {}).get(
                     'address') is not None else {}),
@@ -70,7 +81,7 @@ def inventory_device_uplinks(section: Sequence | None) -> InventoryResult:
             )
 
 
-register.inventory_plugin(
+inventory_plugin_cisco_meraki_org_device_uplinks_info = InventoryPlugin(
     name="cisco_meraki_org_device_uplinks_info",
     inventory_function=inventory_device_uplinks,
 )
diff --git a/source/agent_based/cisco_meraki_org_networks.py b/source/cmk_addons_plugins/meraki/agent_based/networks.py
similarity index 87%
rename from source/agent_based/cisco_meraki_org_networks.py
rename to source/cmk_addons_plugins/meraki/agent_based/networks.py
index 945396da4cb682805dc3c477729a7b7bc86ca7dc..cd98c3ca5154c629dbeb84676b135a24ac5b350d 100644
--- a/source/agent_based/cisco_meraki_org_networks.py
+++ b/source/cmk_addons_plugins/meraki/agent_based/networks.py
@@ -8,23 +8,22 @@
 # Date  : 2023-11-04
 # File  : cisco_meraki_org_networks.py (check plugin)
 
-from dataclasses import dataclass
+# 2024-06-29: refactored for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki_org_networks.py in to networks.py
+
 from collections.abc import Sequence
+from dataclasses import dataclass
 from typing import Final
 
-from cmk.base.plugins.agent_based.agent_based_api.v1 import (
-    TableRow,
-    register,
-)
-from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
+from cmk.agent_based.v2 import (
+    AgentSection,
+    InventoryPlugin,
     InventoryResult,
     StringTable,
+    TableRow,
 )
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    MerakiAPIData,
-    MerakiNetwork,  # type: ignore[import]
-    load_json,
-)
+from cmk_addons.plugins.meraki.lib.utils import MerakiAPIData, MerakiNetwork, load_json
+
 
 _API_NAME_ORGANISATION_NAME: Final = "name"
 
@@ -50,7 +49,7 @@ class NetworkInfo(MerakiNetwork):
     url: str
 
     @classmethod
-    def parse(cls, organisations: MerakiAPIData) -> "NetworkInfo":
+    def parse(cls, organisations: MerakiAPIData):
         networks = []
         for organisation in organisations:
             for network in organisation:
@@ -74,7 +73,7 @@ def parse_meraki_networks(string_table: StringTable) -> Sequence[NetworkInfo] |
     return NetworkInfo.parse(loaded_json) if (loaded_json := load_json(string_table)) else None
 
 
-register.agent_section(
+agent_section_cisco_meraki_org_networks = AgentSection(
     name="cisco_meraki_org_networks",
     parse_function=parse_meraki_networks,
 )
@@ -105,7 +104,7 @@ def inventory_meraki_networks(section: Sequence[NetworkInfo] | None) -> Inventor
         )
 
 
-register.inventory_plugin(
+inventory_plugin_cisco_meraki_org_networks = InventoryPlugin(
     name="cisco_meraki_org_networks",
     inventory_function=inventory_meraki_networks,
 )
diff --git a/source/agent_based/cisco_meraki_organisations_api.py b/source/cmk_addons_plugins/meraki/agent_based/organisations_api.py
similarity index 84%
rename from source/agent_based/cisco_meraki_organisations_api.py
rename to source/cmk_addons_plugins/meraki/agent_based/organisations_api.py
index 130754d79313da306e377948703aef34eef23fb1..dd0660105cbfa798512a1ca45c581b33bf2f969c 100644
--- a/source/agent_based/cisco_meraki_organisations_api.py
+++ b/source/cmk_addons_plugins/meraki/agent_based/organisations_api.py
@@ -11,28 +11,28 @@
 # 2024-04-27: made data parsing more robust
 # 2024-05-12: added api request count
 #             refactoring parse functions as class method
+# 2024-06-29: refactoring for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki_organisations_api.py in to organisations_api.py
 
-from _collections_abc import Mapping, Sequence
+from collections.abc import Mapping, Sequence
 from dataclasses import dataclass
 
-from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+from cmk.agent_based.v2 import (
+    AgentSection,
+    CheckPlugin,
+    CheckResult,
+    DiscoveryResult,
+    InventoryPlugin,
+    InventoryResult,
     Result,
     Service,
     State,
+    StringTable,
     TableRow,
     check_levels,
-    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.cisco_meraki import (
-    get_int,  # type: ignore[import]
-    load_json,
 )
+from cmk_addons.plugins.meraki.lib.utils import add_org_id_name_to_output, get_int, load_json
+
 
 __orgaisations = [
     {'id': '610473',
@@ -122,7 +122,7 @@ def parse_meraki_organisations(string_table: StringTable) -> Mapping[str, Organi
         return {org['id']: Organisation.parse(org) for org in json_data}
 
 
-register.agent_section(
+agent_section_cisco_meraki_org_organisations = AgentSection(
     name='cisco_meraki_org_organisations',
     parsed_section_name='cisco_meraki_organisations_api',
     parse_function=parse_meraki_organisations,
@@ -140,7 +140,7 @@ def parse_cisco_meraki_org_api_requests_by_organization(
         }
 
 
-register.agent_section(
+agent_section_cisco_meraki_org_api_requests_by_organization = AgentSection(
     name='cisco_meraki_org_api_requests_by_organization',
     parsed_section_name='cisco_meraki_org_api_requests_by_organization',
     parse_function=parse_cisco_meraki_org_api_requests_by_organization,
@@ -164,7 +164,7 @@ def inventory_meraki_organisations(section: Mapping[str, Organisation]) -> Inven
         )
 
 
-register.inventory_plugin(
+inventory_plugin_cisco_meraki_organisations_api = InventoryPlugin(
     name='cisco_meraki_organisations_api',
     # sections=['cisco_meraki_organisations'],
     inventory_function=inventory_meraki_organisations,
@@ -202,32 +202,19 @@ def check_organisations_api(
 ) -> CheckResult:
     if (org := section_cisco_meraki_organisations_api.get(params.get('internal_item_name'))) is None:
         return
+
+    yield from add_org_id_name_to_output(
+        org.id,
+        org.name,
+        params['item_variant'],
+        params.get('dont_show_alias_on_info'),
+    )
+
     yield Result(
         state=State.OK if org.api else State(params['state_api_not_enabled']),
-        summary=f'Status: {_api_status[org.api]}',
+        summary=f'({_api_status[org.api]})',
+        details=f'Status: {_api_status[org.api]}',
     )
-    org_id = f'ID: {org.id}'
-    org_name = f'Name: {org.name}'
-    org_id_notice = f'Organisation ID: {org.id}'
-    org_name_notice = f'Organisation name: {org.name}'
-
-    match params['item_variant']:
-        case 'org_id':
-            yield Result(state=State.OK, notice=org_id_notice)
-            if params.get('dont_show_alias_on_info'):
-                yield Result(state=State.OK, notice=org_name_notice)
-            else:
-                yield Result(state=State.OK, summary=org_name)
-        case 'org_name':
-            if params.get('dont_show_alias_on_info'):
-                yield Result(state=State.OK, notice=org_id_notice)
-            else:
-                yield Result(state=State.OK, summary=org_id)
-            yield Result(state=State.OK, notice=org_name_notice)
-
-        case _:
-            yield Result(state=State.OK, notice=org_id_notice)
-            yield Result(state=State.OK, notice=org_name_notice)
 
     if section_cisco_meraki_org_api_requests_by_organization is None or (
             api_requests := section_cisco_meraki_org_api_requests_by_organization.get(params.get(
@@ -266,7 +253,7 @@ def check_organisations_api(
             )
 
 
-register.check_plugin(
+check_plugin_cisco_meraki_organisations_api = CheckPlugin(
     name='cisco_meraki_organisations_api',
     sections=['cisco_meraki_organisations_api', 'cisco_meraki_org_api_requests_by_organization'],
     service_name='Cisco Meraki API %s',
diff --git a/source/agent_based/cisco_meraki_switch_ports_statuses.py b/source/cmk_addons_plugins/meraki/agent_based/switch_ports_statuses.py
similarity index 83%
rename from source/agent_based/cisco_meraki_switch_ports_statuses.py
rename to source/cmk_addons_plugins/meraki/agent_based/switch_ports_statuses.py
index b9673ef5e47292a5355d8d04412df38e717e63a5..8409609a69d4a0c05c47338b0a52cdc90bb2ab44 100644
--- a/source/agent_based/cisco_meraki_switch_ports_statuses.py
+++ b/source/cmk_addons_plugins/meraki/agent_based/switch_ports_statuses.py
@@ -14,32 +14,34 @@
 #             added support for "realtime" traffic counters
 #             refactoring parse functions as class method
 # 2024-05-20: added discovery rule for port status
+# 2024-06-29: refactored for CMK 2.3
+#             fixed render function for bandwidth -> uses now render.networkbandwidth
+#             try to match the output of a "normal" cmk interface service
+# 2024-06-30: renamed from cisco_meraki_switch_ports_statuses.py in to switch_ports_statuses.py
+# 2024-06-30: fixed discovery of (admin disabled) ports
 
+# ToDo: create service label cmk/meraki/uplink:yes/no
 
-from _collections_abc import Mapping, Sequence
+from collections.abc import Mapping, Sequence
 from dataclasses import dataclass
 
-from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+from cmk.agent_based.v2 import (
+    AgentSection,
+    CheckPlugin,
+    CheckResult,
+    DiscoveryResult,
+    InventoryPlugin,
+    InventoryResult,
     Result,
     Service,
     State,
+    StringTable,
     TableRow,
     check_levels,
-    register,
     render,
 )
 
-from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
-    CheckResult,
-    DiscoveryResult,
-    InventoryResult,
-    StringTable,
-)
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    get_float,  # type: ignore[import]
-    get_int,  # type: ignore[import]
-    load_json,
-)
+from cmk_addons.plugins.meraki.lib.utils import get_float, get_int, load_json
 
 
 @dataclass(frozen=True)
@@ -236,7 +238,7 @@ def parse_switch_ports_statuses(string_table: StringTable) -> Mapping[str, Switc
     return {port['portId']: SwitchPort.parse(port) for port in json_data}
 
 
-register.agent_section(
+agent_section_cisco_meraki_org_switch_ports_statuses = AgentSection(
     name="cisco_meraki_org_switch_ports_statuses",
     parse_function=parse_switch_ports_statuses,
 )
@@ -244,18 +246,31 @@ register.agent_section(
 
 def discover_switch_ports_statuses(params: Mapping[str, object], section: Mapping[str, SwitchPort]) -> DiscoveryResult:
     discovered_port_states = params['discovered_port_states']
-    for port in section.keys():
-        if section[port].enabled in discovered_port_states and section[port].status in discovered_port_states:
+    # adjust params, as we can not use True/False as keys anymore in rule sets :-(
+    if 'admin_enabled' in discovered_port_states:
+        discovered_port_states.append(True)
+        discovered_port_states.remove('admin_enabled')
+    if 'admin_disabled' in discovered_port_states:
+        discovered_port_states.append(False)
+        discovered_port_states.append('disabled')
+        discovered_port_states.remove('admin_disabled')
+
+    for port in section.values():
+        if port.enabled in discovered_port_states and port.status.lower() in discovered_port_states:
             yield Service(
-                item=port,
+                item=port.port_id,
                 parameters={
-                    'enabled': section[port].enabled,
-                    'status': section[port].status,
-                    'speed': section[port].speed,
+                    'enabled': port.enabled,
+                    'status': port.status,
+                    'speed': port.speed,
                 }
             )
 
 
+def render_network_bandwidth_bits(value: int) -> str:
+    return render.networkbandwidth(value/8)
+
+
 def check_switch_ports_statuses(item: str, params: Mapping[str, any], section: Mapping[str, SwitchPort]) -> CheckResult:
     def _status_changed(is_state: str, was_state: str, state: int, message: str):
         if is_state != was_state:
@@ -263,9 +278,7 @@ def check_switch_ports_statuses(item: str, params: Mapping[str, any], section: M
             was_state = was_state if was_state else 'N/A'
             yield Result(state=State(state), notice=f'{message}: from {was_state}, to {is_state}')
 
-    try:
-        port: SwitchPort = section[item]
-    except KeyError:
+    if (port := section.get(item)) is None:
         return
 
     # check admin state changed
@@ -286,7 +299,7 @@ def check_switch_ports_statuses(item: str, params: Mapping[str, any], section: M
             state=params['state_op_change']
         )
         if port.status.lower() == 'connected':  # check operational state
-            yield Result(state=State.OK, notice=f'Operational status: {port.status}')
+            yield Result(state=State.OK, summary=f'({port.status})', details=f'Operational status: {port.status}')
             # check speed changed
             yield from _status_changed(
                 is_state=port.speed,
@@ -296,34 +309,43 @@ def check_switch_ports_statuses(item: str, params: Mapping[str, any], section: M
             )
             if params['speed'] == port.speed:  # only if speed unchanged
                 yield Result(state=State.OK, summary=f'Speed: {port.speed}')
-            if port.duplex.lower() == 'full':  # check duplex state
-                yield Result(state=State.OK, summary=f'Duplex: {port.duplex}')
-            else:
-                yield Result(state=State(params['state_not_full_duplex']), summary=f'Duplex: {port.duplex}')
-            yield Result(state=State.OK, summary=f'Clients: {port.client_count}')
 
             if params.get('show_traffic'):
-                yield from check_levels(
-                    value=port.traffic.sent,  # Bits
-                    label='Out',
-                    metric_name='if_out_bps',
-                    render_func=lambda v: render.networkbandwidth(v/8),  # Bytes
-                    # notice_only=True,
-                )
                 yield from check_levels(
                     value=port.traffic.recv,  # Bits
                     label='In',
                     metric_name='if_in_bps',
-                    render_func=lambda v: render.networkbandwidth(v/8),  # Bytes
+                    render_func=render_network_bandwidth_bits,  # Bytes
+                    # notice_only=True,
+                )
+                yield from check_levels(
+                    value=port.traffic.sent,  # Bits
+                    label='Out',
+                    metric_name='if_out_bps',
+                    render_func=render_network_bandwidth_bits,  # Bytes
                     # notice_only=True,
                 )
+
+            if port.duplex.lower() == 'full':  # check duplex state
+                yield Result(state=State.OK, notice=f'Duplex: {port.duplex}')
+            else:
+                yield Result(state=State(params['state_not_full_duplex']), notice=f'Duplex: {port.duplex}')
+            yield Result(state=State.OK, notice=f'Clients: {port.client_count}')
         else:
-            yield Result(state=State(params['state_not_connected']), summary=f'Operational status: {port.status}')
+            yield Result(
+                state=State(params['state_not_connected']),
+                summary=f'({port.status})',
+                details=f'Operational status: {port.status}'
+            )
     else:
-        yield Result(state=State(params['state_disabled']), summary=f'Admin status: {_admin_status[port.enabled]}')
+        yield Result(
+            state=State(params['state_disabled']),
+            summary=f'({_admin_status[port.enabled].title()})',
+            details=f'Admin status: {_admin_status[port.enabled].title()}',
+        )
 
     if port.is_up_link:
-        yield Result(state=State.OK, summary=f'UP-Link: {_is_up_link[port.is_up_link]}')
+        yield Result(state=State.OK, summary='UP-Link', details=f'UP-Link: {_is_up_link[port.is_up_link]}')
     else:
         yield Result(state=State.OK, notice=f'UP-Link: {_is_up_link[port.is_up_link]}')
 
@@ -344,7 +366,7 @@ def check_switch_ports_statuses(item: str, params: Mapping[str, any], section: M
         yield Result(state=State.UNKNOWN, summary=f'Secure Port enabled', details=f'Secure Port: {port.secure_port}')
 
 
-register.check_plugin(
+check_plugin_cisco_meraki_org_switch_ports_statuses = CheckPlugin(
     name='cisco_meraki_org_switch_ports_statuses',
     service_name='Port %s',
     discovery_function=discover_switch_ports_statuses,
@@ -360,7 +382,7 @@ register.check_plugin(
     check_ruleset_name='cisco_meraki_switch_ports_statuses',
     discovery_ruleset_name='discovery_cisco_meraki_switch_ports_statuses',
     discovery_default_parameters={
-        'discovered_port_states': [True, False, 'Connected', 'Disconnected']
+        'discovered_port_states': ['admin_enabled', 'admin_disabled', 'connected', 'disconnected']
     }
 )
 
@@ -388,7 +410,7 @@ def inventory_meraki_cdp_cache(section: Mapping[str, SwitchPort]) -> InventoryRe
             )
 
 
-register.inventory_plugin(
+inventory_plugin_inv_meraki_cdp_cache = InventoryPlugin(
     name='inv_meraki_cdp_cache',
     sections=['cisco_meraki_org_switch_ports_statuses'],
     inventory_function=inventory_meraki_cdp_cache,
@@ -419,7 +441,7 @@ def inventory_meraki_lldp_cache(section: Mapping[str, SwitchPort]) -> InventoryR
             )
 
 
-register.inventory_plugin(
+inventory_plugin_inv_meraki_lldp_cache = InventoryPlugin(
     name='inv_meraki_lldp_cache',
     sections=['cisco_meraki_org_switch_ports_statuses'],
     inventory_function=inventory_meraki_lldp_cache,
diff --git a/source/agent_based/cisco_meraki_org_wireless_device_status.py b/source/cmk_addons_plugins/meraki/agent_based/wireless_device_ssid_status.py
similarity index 88%
rename from source/agent_based/cisco_meraki_org_wireless_device_status.py
rename to source/cmk_addons_plugins/meraki/agent_based/wireless_device_ssid_status.py
index 754262834711302c1d5f9890dfe71407d6f2081b..16b03451be45fb0311b8923929265eb0a2eb974c 100644
--- a/source/agent_based/cisco_meraki_org_wireless_device_status.py
+++ b/source/cmk_addons_plugins/meraki/agent_based/wireless_device_ssid_status.py
@@ -11,27 +11,24 @@
 # 2024-04-27: made data parsing more robust
 # 2024-06-23: fixed crash on empty json_data
 #             moved data parsing in to SSID class
+# 2024-06-30: renamed from cisco_meraki_org_wireless_device_status.py int to wireless_device_ssid_status.py
 
 
-from _collections_abc import Mapping
+from collections.abc import Mapping
 from dataclasses import dataclass
 
-from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+from cmk.agent_based.v2 import (
+    AgentSection,
+    CheckPlugin,
+    CheckResult,
+    DiscoveryResult,
     Metric,
     Result,
     Service,
     State,
-    register,
-)
-from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
-    CheckResult,
-    DiscoveryResult,
     StringTable,
 )
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    get_int,  # type: ignore[import]
-    load_json,
-)
+from cmk_addons.plugins.meraki.lib.utils import get_int, load_json
 
 
 @dataclass(frozen=True)
@@ -83,7 +80,7 @@ def parse_wireless_device_status(string_table: StringTable) -> Mapping[str, SSID
     return ssids
 
 
-register.agent_section(
+agent_section_cisco_meraki_org_wireless_device_status = AgentSection(
     name="cisco_meraki_org_wireless_device_status",
     parse_function=parse_wireless_device_status,
 )
@@ -104,10 +101,8 @@ def check_wireless_device_status(
         params: Mapping[str, any],
         section: Mapping[str, SSID]
 ) -> CheckResult:
-    try:
-        ssid: SSID = section[item]
-    except KeyError:
-        return None
+    if (ssid := section.get(item)) is None:
+        return
 
     yield Result(state=State.OK, summary=f'Name: {ssid.name}')
     if not ssid.enabled:
@@ -123,12 +118,12 @@ def check_wireless_device_status(
         yield Result(state=State.OK, summary=f'Channel: {ssid.channel}')
         yield Metric(name='channel', value=ssid.channel)
         yield Result(state=State.OK, summary=f'Channel width: {ssid.channel_width}')
-        yield Metric(name='channel_width', value=int(ssid.channel_width.split(' ')[0]) * 1000000)  # change to Hz from MHz
+        yield Metric(name='channel_width', value=int(ssid.channel_width.split(' ')[0]) * 1000000)  # change MHz -> Hz
         yield Result(state=State.OK, summary=f'Power: {ssid.power}')
         yield Metric(name='signal_power', value=int(ssid.power.split(' ')[0]))
 
 
-register.check_plugin(
+check_plugin_cisco_meraki_org_wireless_device_status = CheckPlugin(
     name='cisco_meraki_org_wireless_device_status',
     service_name='SSID %s',
     discovery_function=discover_wireless_device_status,
@@ -138,4 +133,3 @@ register.check_plugin(
     },
     check_ruleset_name='cisco_meraki_wireless_device_status',
 )
-
diff --git a/source/agent_based/cisco_meraki_org_wireless_ethernet_statuses.py b/source/cmk_addons_plugins/meraki/agent_based/wireless_ethernet_statuses.py
similarity index 75%
rename from source/agent_based/cisco_meraki_org_wireless_ethernet_statuses.py
rename to source/cmk_addons_plugins/meraki/agent_based/wireless_ethernet_statuses.py
index 046fd9a6a49a94f7cd7de066ad0a3d25047e62b5..9fc2fca6106790c47ce61a41cc03f5c988a2e5c0 100644
--- a/source/agent_based/cisco_meraki_org_wireless_ethernet_statuses.py
+++ b/source/cmk_addons_plugins/meraki/agent_based/wireless_ethernet_statuses.py
@@ -9,26 +9,27 @@
 # File  : cisco_meraki_org_wireless_ethernet_statuses.py (check plugin)
 
 # 2024-04-27: made data parsing more robust
+# 2024-06-29: refactored for CMK 2.3
+#             moved parse functions to class methods
+# 2024-06-30: renamed from cisco_meraki_org_wireless_ethernet_statuses.py in to wireless_ethernet_statuses.py
 
+# ToDo: create ruleset cisco_meraki_wireless_ethernet_statuses
+
+from collections.abc import Mapping
 from dataclasses import dataclass
-from _collections_abc import Mapping
 
-from cmk.base.plugins.agent_based.agent_based_api.v1 import (
+from cmk.agent_based.v2 import (
+    AgentSection,
+    CheckPlugin,
+    CheckResult,
+    DiscoveryResult,
     Result,
     Service,
     State,
-    register,
-    render,
-)
-from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import (
-    CheckResult,
-    DiscoveryResult,
     StringTable,
+    render,
 )
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    get_int,  # type: ignore[import]
-    load_json,
-)
+from cmk_addons.plugins.meraki.lib.utils import get_int, load_json
 
 __ethernet_port_statuses = {
     'aggregation': {
@@ -70,6 +71,14 @@ class WirelessEthernetPortPower:
     ac: bool | None
     poe: bool | None
 
+    @classmethod
+    def parse(cls, power: Mapping[str, object]):
+        return cls(
+            mode=str(power['mode']) if power.get('mode') is not None else None,
+            ac=bool(power['ac']['isConnected']) if power.get('ac', {}).get('isConnected') is not None else None,
+            poe=bool(power['poe']['isConnected']) if power.get('poe', {}).get('isConnected') is not None else None,
+        )
+
 
 @dataclass(frozen=True)
 class WirelessEthernetPort:
@@ -79,6 +88,19 @@ class WirelessEthernetPort:
     speed: int | None
     power: WirelessEthernetPortPower | None
 
+    @classmethod
+    def parse(cls, port: Mapping[str, object], power: WirelessEthernetPortPower):
+        return cls(
+            name=str(port['name']) if port.get('name') is not None else None,
+            power=power,
+            duplex=port['linkNegotiation']['duplex'] if port.get('linkNegotiation', {}).get(
+                'duplex') is not None else None,
+            # changed to bit/s
+            speed=int(port['linkNegotiation']['speed']) * 125000 if get_int(port.get('linkNegotiation', {}).get(
+                'speed')) else None,
+            poe=str(port['poe']['standard']) if port.get('poe', {}).get('standard') is not None else None,
+        )
+
 
 _is_connected = {
     True: 'Yes',
@@ -90,29 +112,12 @@ def parse_wireless_ethernet_statuses(string_table: StringTable) -> Mapping[str,
     json_data = load_json(string_table)
     json_data = json_data[0]
 
-    power = WirelessEthernetPortPower(
-        mode=str(json_data['power']['mode']) if json_data['power'].get('mode') is not None else None,
-        ac=bool(json_data['power']['ac']['isConnected']) if json_data['power'].get('ac', {}).get(
-            'isConnected') is not None else None,
-        poe=bool(json_data['power']['poe']['isConnected']) if json_data['power'].get('poe', {}).get(
-            'isConnected') is not None else None,
-    ) if json_data.get('power') is not None else None
+    power = WirelessEthernetPortPower.parse(json_data['power']) if json_data.get('power') is not None else None
 
-    return {
-        port['name']: WirelessEthernetPort(
-            name=str(port['name']) if port.get('name') is not None else None,
-            power=power,
-            duplex=port['linkNegotiation']['duplex'] if port.get('linkNegotiation', {}).get(
-                'duplex') is not None else None,
-            # changed to bit/s
-            speed=int(port['linkNegotiation']['speed']) * 125000 if get_int(port.get('linkNegotiation', {}).get(
-                'speed')) else None,
-            poe=str(port['poe']['standard']) if port.get('poe', {}).get('standard') is not None else None,
-        ) for port in json_data.get('ports', [])
-    }
+    return {port['name']: WirelessEthernetPort.parse(port, power) for port in json_data.get('ports', [])}
 
 
-register.agent_section(
+agent_section_cisco_meraki_org_wireless_ethernet_statuses = AgentSection(
     name="cisco_meraki_org_wireless_ethernet_statuses",
     parse_function=parse_wireless_ethernet_statuses,
 )
@@ -132,9 +137,7 @@ def check_wireless_ethernet_statuses(
         if is_state != was_state:
             yield Result(state=State(state), notice=f'{message}: is {is_state}, was {was_state}')
 
-    try:
-        port: WirelessEthernetPort = section[item]
-    except KeyError:
+    if (port := section.get(item)) is None:
         return None
 
     if port.speed:
@@ -171,7 +174,7 @@ def check_wireless_ethernet_statuses(
     yield Result(state=State.OK, summary=f'PoE standard: {port.poe}')
 
 
-register.check_plugin(
+check_plugin_cisco_meraki_org_wireless_ethernet_statuses = CheckPlugin(
     name='cisco_meraki_org_wireless_ethernet_statuses',
     service_name='Port %s',
     discovery_function=discover_wireless_ethernet_statuses,
@@ -182,5 +185,5 @@ register.check_plugin(
         'state_no_speed': 1,
         'state_speed_change': 1,
     },
-    check_ruleset_name='cisco_meraki_wireless_ethernet_statuses',
+    # check_ruleset_name='cisco_meraki_wireless_ethernet_statuses',
 )
diff --git a/source/cmk_addons_plugins/meraki/graphing/packages.py b/source/cmk_addons_plugins/meraki/graphing/packages.py
new file mode 100644
index 0000000000000000000000000000000000000000..108400b33d1cbba8e673a387f1483698335e7b4e
--- /dev/null
+++ b/source/cmk_addons_plugins/meraki/graphing/packages.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-11-04
+# File  : cisco_meraki.py (metrics)
+#
+# 2023-11-12: added wireless device status (channel, channel width, signal power)
+# 2024-05-12: added switch port statuses and API return Codes
+# 2024-06-24: fixed, wrong total for SSID perfometer signal_power -> 30
+# 2024-06-27: refactored for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki.py in to packages.py
+
+from cmk.graphing.v1 import Title, graphs, metrics, perfometers
+
+#
+# unit definitions
+#
+UNIT_DBM = metrics.Unit(metrics.DecimalNotation('dBm'))
+UNIT_HZ = metrics.Unit(metrics.SINotation('hZ'))
+UNIT_NUMBER = metrics.Unit(metrics.DecimalNotation(''))
+UNIT_PERCENT = metrics.Unit(metrics.DecimalNotation('%'))
+UNIT_TIME = metrics.Unit(metrics.TimeNotation())
+#
+# license overview
+#
+metric_sum_licensed_devices = metrics.Metric(
+    name='sum_licensed_devices',
+    title=Title('Licensed devices'),
+    unit=UNIT_NUMBER,
+    color=metrics.Color.LIGHT_GREEN,
+)
+
+metric_remaining_time = metrics.Metric(
+    name='remaining_time',
+    title=Title('Remaining time'),
+    unit=UNIT_TIME,
+    color=metrics.Color.GREEN,
+)
+
+graph_cisco_meraki_remaining_time = graphs.Graph(
+    name='cisco_meraki.remaining_time',
+    title=Title('Cisco Meraki Licenses remaining time'),
+    compound_lines=['remaining_time'],
+    simple_lines=[
+        metrics.WarningOf("remaining_time"),
+        metrics.CriticalOf("remaining_time"),
+    ],
+    minimal_range=graphs.MinimalRange(0, 180),
+)
+
+graph_cisco_meraki_licensed_devices = graphs.Graph(
+    name='cisco_meraki_licensed_devices',
+    title=Title('Cisco Meraki Licensed devices'),
+    compound_lines=['sum_licensed_devices'],
+    minimal_range=graphs.MinimalRange(0, 10),
+)
+
+perfometer_licensing = perfometers.Stacked(
+    name="merak_licensing",
+    # upper and lower are in the wrong order
+    lower=perfometers.Perfometer(
+        name='sum_licensed_devices',
+        focus_range=perfometers.FocusRange(perfometers.Open(0), perfometers.Open(100)),
+        segments=['sum_licensed_devices'],
+    ),
+    upper=perfometers.Perfometer(
+        name='remaining_time',
+        focus_range=perfometers.FocusRange(perfometers.Open(0), perfometers.Open(180)),
+        segments=["remaining_time"],
+    ),
+)
+
+#
+# wireless devices status
+#
+metric_signal_power = metrics.Metric(
+    name='signal_power',
+    title=Title("Power"),
+    unit=UNIT_DBM,
+    color=metrics.Color.GREEN,
+)
+
+metric_channel_width = metrics.Metric(
+    name="channel_width",
+    title=Title("Channel Width"),
+    unit=UNIT_HZ,
+    color=metrics.Color.BLUE,
+)
+
+metric_channel = metrics.Metric(
+    name="channel",
+    title=Title("Channel"),
+    unit=UNIT_NUMBER,
+    color=metrics.Color.DARK_YELLOW,
+)
+
+graph_cisco_meraki_wireless_device_status_signal_power = graphs.Graph(
+    name='cisco_meraki_wireless_device_status_signal_power',
+    title=Title('Signal power'),
+    compound_lines=['signal_power'],
+    minimal_range=graphs.MinimalRange(0, 'signal_power:max'),
+)
+
+graph_cisco_meraki_wireless_device_status_channel_width = graphs.Graph(
+    name='cisco_meraki_wireless_device_status_channel_width',
+    title=Title('Channel Width'),
+    compound_lines=['channel_width'],
+    minimal_range=graphs.MinimalRange(0, 'channel_width:max'),
+)
+
+graph_cisco_meraki_wireless_device_status_channel = graphs.Graph(
+    name='cisco_meraki_wireless_device_status_channel',
+    title=Title('Channel'),
+    compound_lines=['channel'],
+    minimal_range=graphs.MinimalRange(0, 'channel:max'),
+)
+
+perfometer_signal_power = perfometers.Perfometer(
+    name='signal_power',
+    segments=['signal_power'],
+    focus_range=perfometers.FocusRange(perfometers.Closed(0), perfometers.Closed(40))
+)
+
+#
+# API return Codes
+#
+metric_api_code_2xx = metrics.Metric(
+    name="api_code_2xx",
+    title=Title("Code 2xx"),
+    unit=UNIT_NUMBER,
+    color=metrics.Color.LIGHT_GREEN,
+)
+
+metric_api_code_3xx = metrics.Metric(
+    name="api_code_3xx",
+    title=Title("Code 3xx"),
+    unit=UNIT_NUMBER,
+    color=metrics.Color.LIGHT_BLUE,
+)
+
+metric_api_code_4xx = metrics.Metric(
+    name="api_code_4xx",
+    title=Title("Code 4xx"),
+    unit=UNIT_NUMBER,
+    color=metrics.Color.LIGHT_RED,
+)
+
+metric_api_code_5xx = metrics.Metric(
+    name="api_code_5xx",
+    title=Title("Code 5xx"),
+    unit=UNIT_NUMBER,
+    color=metrics.Color.DARK_RED,
+)
+
+graph_cisco_meraki_cisco_meraki_organisations_api_code = graphs.Bidirectional(
+    name='cisco_meraki_cisco_meraki_organisations_api_code',
+    title=Title('Cisco Meraki API response codes'),
+    upper=graphs.Graph(
+        name='api_code_ok',
+        title=Title('Cisco Meraki API response codes'),
+        simple_lines=[
+            'api_code_2xx',
+            'api_code_3xx',
+        ],
+        optional=[
+            'api_code_2xx',
+            'api_code_3xx',
+        ]
+    ),
+    lower=graphs.Graph(
+        name='api_codebad',
+        title=Title('Cisco Meraki API response codes'),
+        simple_lines=[
+            'api_code_4xx',
+            'api_code_5xx',
+        ],
+        optional=[
+            'api_code_4xx',
+            'api_code_5xx',
+        ]
+    ),
+)
+
+perfometer_api_code = perfometers.Stacked(
+    name='api_code',
+    lower=perfometers.Perfometer(
+        name='api_code_2xx',
+        focus_range=perfometers.FocusRange(perfometers.Closed(0), perfometers.Open(30)),
+        segments=["api_code_2xx"],
+    ),
+    upper=perfometers.Perfometer(
+        name='api_code_4xx',
+        focus_range=perfometers.FocusRange(perfometers.Closed(0), perfometers.Open(30)),
+        segments=["api_code_4xx"],
+    )
+)
+
+perfometer_api_code_2xx = perfometers.Perfometer(
+    name='api_code_2xx',
+    segments=["api_code_2xx"],
+    focus_range=perfometers.FocusRange(perfometers.Closed(0), perfometers.Open(50)),
+)
+
+perfometer_api_code_4xx = perfometers.Perfometer(
+    name='api_code_4xx',
+    segments=['api_code_4xx'],
+    focus_range=perfometers.FocusRange(perfometers.Closed(0), perfometers.Open(50)),
+)
+
+#
+# appliance performance/utilization
+#
+metric_utilization = metrics.Metric(
+    name="utilization",
+    title=Title("Utilization"),
+    unit=UNIT_PERCENT,
+    color=metrics.Color.LIGHT_GREEN,
+)
+
+graph_cisco_meraki_cisco_meraki_appliance_utilization = graphs.Graph(
+    name='cisco_meraki_cisco_meraki_appliance_utilization',
+    title=Title('Cisco Meraki Appliance Utilization'),
+    compound_lines=['utilization'],
+    simple_lines=[
+        metrics.WarningOf("utilization"),
+        metrics.CriticalOf("utilization"),
+    ],
+    minimal_range=graphs.MinimalRange(0, 100),
+)
+
+perfometer_utilization = perfometers.Perfometer(
+    name='utilization',
+    segments=['utilization'],
+    focus_range=perfometers.FocusRange(perfometers.Closed(0), perfometers.Closed(100)),
+)
diff --git a/source/lib/python3/cmk/special_agents/agent_cisco_meraki.py b/source/cmk_addons_plugins/meraki/lib/agent.py
similarity index 95%
rename from source/lib/python3/cmk/special_agents/agent_cisco_meraki.py
rename to source/cmk_addons_plugins/meraki/lib/agent.py
index e1bd3fcbd0e29683dc1317547e6e99e003439519..860cc46d65c7acac1a41802e04d97410dd8efb0e 100644
--- a/source/lib/python3/cmk/special_agents/agent_cisco_meraki.py
+++ b/source/cmk_addons_plugins/meraki/lib/agent.py
@@ -37,8 +37,6 @@
 # 2024-05-20: made appliance uplinks usage user selectable
 #             made API requests per org user selectable
 # 2024-06-23: added cache time per section -> not nice but should work.
-# 2024-06-24: renamed cache time per section option form --cache_per_section to --cache-per-section
-#             fixed --cache-per-section parameter evaluation
 
 # ToDo: create inventory from Networks, is per organisation, not sure where/how to put this in the inventory
 # ToDo: list Connected Datacenters like Umbrella https://developer.cisco.com/meraki/api-v1/list-data-centers/
@@ -64,45 +62,66 @@ from enum import auto, Enum
 from logging import getLogger
 from os import environ
 from pathlib import Path
-from random import randrange
+# from random import randrange
 from requests import request, RequestException
 from time import strftime, gmtime, time as now_time
 from time import time_ns
-from typing import Final, TypedDict, Any
+from typing import Final, TypedDict, Any, List
 # from urllib.request import getproxies
 
 import meraki  # type: ignore[import]
 
 from cmk.utils.paths import tmp_dir
 
-from cmk.special_agents.utils.agent_common import (
+from cmk.special_agents.v0_unstable.agent_common import (
     ConditionalPiggybackSection,
     SectionWriter,
     special_agent_main,
 )
-from cmk.special_agents.utils.argument_parsing import create_default_argument_parser  # , Args
-from cmk.special_agents.utils.misc import DataCache
-
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    MerakiNetwork,  # type: ignore[import]
-    _SEC_NAME_APPLIANCE_UPLINKS,  # type: ignore[import]
-    _SEC_NAME_APPLIANCE_UPLINKS_USAGE,  # type: ignore[import]
-    _SEC_NAME_APPLIANCE_VPNS,  # type: ignore[import]
-    _SEC_NAME_APPLIANCE_PERFORMANCE,  # type: ignore[import]
-    _SEC_NAME_CELLULAR_UPLINKS,  # type: ignore[import]
-    _SEC_NAME_DEVICE_INFO,  # type: ignore[import]
-    _SEC_NAME_DEVICE_STATUSES,  # type: ignore[import]
-    _SEC_NAME_DEVICE_UPLINKS_INFO,  # type: ignore[import]
-    _SEC_NAME_LICENSES_OVERVIEW,  # type: ignore[import]
-    _SEC_NAME_NETWORKS,  # type: ignore[import]
-    _SEC_NAME_ORGANISATIONS,  # type: ignore[import]
-    _SEC_NAME_ORG_API_REQUESTS,  # type: ignore[import]
-    _SEC_NAME_SENSOR_READINGS,  # type: ignore[import]
-    _SEC_NAME_SWITCH_PORTS_STATUSES,  # type: ignore[import]
-    _SEC_NAME_WIRELESS_DEVICE_STATUS,  # type: ignore[import]
-    _SEC_NAME_WIRELESS_ETHERNET_STATUSES,  # type: ignore[import]
+from cmk.special_agents.v0_unstable.argument_parsing import create_default_argument_parser  # , Args
+from cmk.special_agents.v0_unstable.misc import DataCache
+
+from cmk_addons.plugins.meraki.lib.utils import (
+    MerakiNetwork,
+
+    # parameter names
+    _SEC_NAME_APPLIANCE_UPLINKS,
+    _SEC_NAME_APPLIANCE_UPLINKS_USAGE,
+    _SEC_NAME_APPLIANCE_VPNS,
+    _SEC_NAME_APPLIANCE_PERFORMANCE,
+    _SEC_NAME_CELLULAR_UPLINKS,
+    _SEC_NAME_DEVICE_INFO,
+    _SEC_NAME_DEVICE_STATUSES,
+    _SEC_NAME_DEVICE_UPLINKS_INFO,
+    _SEC_NAME_LICENSES_OVERVIEW,
+    _SEC_NAME_NETWORKS,
+    _SEC_NAME_ORGANISATIONS,
+    _SEC_NAME_ORG_API_REQUESTS,
+    _SEC_NAME_SENSOR_READINGS,
+    _SEC_NAME_SWITCH_PORTS_STATUSES,
+    _SEC_NAME_WIRELESS_DEVICE_STATUS,
+    _SEC_NAME_WIRELESS_ETHERNET_STATUSES,
     # Early Access
-    _SEC_NAME_ORG_SWITCH_PORTS_STATUSES,  # type: ignore[import]
+    _SEC_NAME_ORG_SWITCH_PORTS_STATUSES,
+
+    # api cache defaults per section
+    _SEC_CACHE_APPLIANCE_PERFORMANCE,
+    _SEC_CACHE_APPLIANCE_UPLINKS_USAGE,
+    _SEC_CACHE_APPLIANCE_UPLINKS,
+    _SEC_CACHE_APPLIANCE_VPNS,
+    _SEC_CACHE_CELLULAR_UPLINKS,
+    _SEC_CACHE_DEVICE_INFO,
+    _SEC_CACHE_DEVICE_STATUSES,
+    _SEC_CACHE_DEVICE_UPLINKS_INFO,
+    _SEC_CACHE_LICENSES_OVERVIEW,
+    _SEC_CACHE_NETWORKS,
+    _SEC_CACHE_ORG_API_REQUESTS,
+    _SEC_CACHE_ORG_SWITCH_PORTS_STATUSES,
+    _SEC_CACHE_ORGANISATIONS,
+    _SEC_CACHE_SENSOR_READINGS,
+    _SEC_CACHE_SWITCH_PORTS_STATUSES,
+    _SEC_CACHE_WIRELESS_DEVICE_STATUS,
+    _SEC_CACHE_WIRELESS_ETHERNET_STATUSES,
 )
 
 _LOGGER = getLogger("agent_cisco_meraki")
@@ -122,6 +141,7 @@ _API_NAME_NETWORK_ID: Final = 'networkId'
 _API_NAME_ORGANISATION_ID: Final = "id"
 _API_NAME_ORGANISATION_NAME: Final = "name"
 
+# map section parameter name to python name (do we really need this, why not use the name ("-" -> "_")?
 _SECTION_NAME_MAP = {
     _SEC_NAME_APPLIANCE_UPLINKS: "appliance_uplinks",
     _SEC_NAME_APPLIANCE_UPLINKS_USAGE: "appliance_uplinks_usage",
@@ -845,7 +865,6 @@ class MerakiOrganisation:
                             piggyback=self._adjust_piggyback(host=piggyback),
                             )
 
-
         if devices_by_type.get(_API_NAME_DEVICE_TYPE_SWITCH):
             if _SEC_NAME_SWITCH_PORTS_STATUSES not in self.config.excluded_sections:
                 for switch in devices_by_type[_API_NAME_DEVICE_TYPE_SWITCH]:
@@ -1102,7 +1121,7 @@ def parse_arguments(argv: Sequence[str] | None) -> Args:
 
     parser.add_argument(
         "--excluded-sections",
-        nargs="+",
+        nargs="*",
         choices=list(_SECTION_NAME_MAP),
         default=[],
         help="Sections that are excluded form data collected.",
@@ -1144,8 +1163,26 @@ def parse_arguments(argv: Sequence[str] | None) -> Args:
         '--cache-per-section',
         nargs="+",
         type=int,
-        help="List of chache time per section in minutes",
-        default=[0, 0, 60, 60, 60, 60, 60, 60, 600, 600, 0, 0, 600, 0, 0, 30, 30]
+        help="List of cache time per section in minutes",
+        default=[
+            _SEC_CACHE_APPLIANCE_PERFORMANCE,
+            _SEC_CACHE_APPLIANCE_UPLINKS_USAGE,
+            _SEC_CACHE_APPLIANCE_UPLINKS,
+            _SEC_CACHE_APPLIANCE_VPNS,
+            _SEC_CACHE_CELLULAR_UPLINKS,
+            _SEC_CACHE_DEVICE_INFO,
+            _SEC_CACHE_DEVICE_STATUSES,
+            _SEC_CACHE_DEVICE_UPLINKS_INFO,
+            _SEC_CACHE_LICENSES_OVERVIEW,
+            _SEC_CACHE_NETWORKS,
+            _SEC_CACHE_ORG_API_REQUESTS,
+            _SEC_CACHE_ORG_SWITCH_PORTS_STATUSES,
+            _SEC_CACHE_ORGANISATIONS,
+            _SEC_CACHE_SENSOR_READINGS,
+            _SEC_CACHE_SWITCH_PORTS_STATUSES,
+            _SEC_CACHE_WIRELESS_DEVICE_STATUS,
+            _SEC_CACHE_WIRELESS_ETHERNET_STATUSES
+        ]
     )
 
     return parser.parse_args(argv)
diff --git a/source/agent_based/utils/cisco_meraki.py b/source/cmk_addons_plugins/meraki/lib/utils.py
similarity index 65%
rename from source/agent_based/utils/cisco_meraki.py
rename to source/cmk_addons_plugins/meraki/lib/utils.py
index 2ada91fd7af1419d431f4256556b634961e96d06..810af8679a6934857233ada91dab85ca4687af15 100644
--- a/source/agent_based/utils/cisco_meraki.py
+++ b/source/cmk_addons_plugins/meraki/lib/utils.py
@@ -8,6 +8,8 @@
 # - changed check_last_reported_ts to output report as metric/levels
 # - added levels_upper check_last_reported_ts
 # - added section names from the cisco meraki special agent (for use in WATO)
+# 2024-06-30: moved to cmk_addons/plugins/meraki/lib
+#             renamed from cisco_meraki.py to utils.py
 
 import json
 import time
@@ -20,6 +22,7 @@ from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import CheckResul
 
 MerakiAPIData = Mapping[str, object]
 
+# parameter names for agent options
 _SEC_NAME_ORGANISATIONS: Final = "_organisations"  # internal use runs always
 _SEC_NAME_DEVICE_INFO: Final = "_device_info"  # Not configurable, needed for piggyback
 _SEC_NAME_NETWORKS: Final = "_networks"  # internal use, runs always, needed for network names
@@ -30,7 +33,7 @@ _SEC_NAME_APPLIANCE_UPLINKS_USAGE: Final = "appliance-uplinks-usage"
 _SEC_NAME_APPLIANCE_VPNS: Final = "appliance-vpns"
 _SEC_NAME_APPLIANCE_PERFORMANCE: Final = "appliance-performance"
 _SEC_NAME_CELLULAR_UPLINKS: Final = "cellular-uplinks"
-_SEC_NAME_DEVICE_STATUSES: Final = "device-statuses"
+_SEC_NAME_DEVICE_STATUSES: Final = "device-status"
 _SEC_NAME_DEVICE_UPLINKS_INFO: Final = "device-uplinks-info"
 _SEC_NAME_LICENSES_OVERVIEW: Final = "licenses-overview"
 _SEC_NAME_SENSOR_READINGS: Final = "sensor-readings"
@@ -39,6 +42,27 @@ _SEC_NAME_WIRELESS_DEVICE_STATUS: Final = "wireless-device-status"
 _SEC_NAME_WIRELESS_ETHERNET_STATUSES: Final = "wireless-ethernet-statuses"
 
 
+# api cache defaults per section
+_SEC_CACHE_APPLIANCE_PERFORMANCE = 0
+_SEC_CACHE_APPLIANCE_UPLINKS_USAGE = 0
+_SEC_CACHE_APPLIANCE_UPLINKS = 60
+_SEC_CACHE_APPLIANCE_VPNS = 60
+_SEC_CACHE_CELLULAR_UPLINKS = 60
+_SEC_CACHE_DEVICE_INFO = 60
+_SEC_CACHE_DEVICE_STATUSES = 60
+_SEC_CACHE_DEVICE_UPLINKS_INFO = 60
+_SEC_CACHE_LICENSES_OVERVIEW = 600
+_SEC_CACHE_NETWORKS = 600
+_SEC_CACHE_ORG_API_REQUESTS = 0
+_SEC_CACHE_ORG_SWITCH_PORTS_STATUSES = 0
+_SEC_CACHE_ORGANISATIONS = 600
+_SEC_CACHE_SENSOR_READINGS = 0
+_SEC_CACHE_SWITCH_PORTS_STATUSES = 0
+_SEC_CACHE_WIRELESS_DEVICE_STATUS = 30
+_SEC_CACHE_WIRELESS_ETHERNET_STATUSES = 30
+
+
+
 # Early Access
 _SEC_NAME_ORG_SWITCH_PORTS_STATUSES: Final = "org-switch-ports-statuses"
 
@@ -100,3 +124,33 @@ def get_float(value: str | None) -> float | None:
         return float(value)
     except TypeError:
         return
+
+
+def add_org_id_name_to_output(
+        organisation_id: str,
+        organisation_name: str,
+        item_variant: str,
+        dont_show_alias_on_info: bool,
+) -> GeneratorExit:
+    org_id = f'[{organisation_id}]'
+    org_name = f'[{organisation_name}]'
+    org_id_notice = f'Organisation ID: {organisation_id}'
+    org_name_notice = f'Organisation name: {organisation_name}'
+
+    match item_variant:
+        case 'org_id':
+            yield Result(state=State.OK, notice=org_id_notice)
+            if dont_show_alias_on_info:
+                yield Result(state=State.OK, notice=org_name_notice)
+            else:
+                yield Result(state=State.OK, summary=org_name, details=org_name_notice)
+        case 'org_name':
+            if dont_show_alias_on_info:
+                yield Result(state=State.OK, notice=org_id_notice)
+            else:
+                yield Result(state=State.OK, summary=org_id, details=org_id_notice)
+            yield Result(state=State.OK, notice=org_name_notice)
+
+        case _:
+            yield Result(state=State.OK, notice=org_id_notice)
+            yield Result(state=State.OK, notice=org_name_notice)
\ No newline at end of file
diff --git a/source/cmk_addons_plugins/meraki/rulesets/appliance_performance.py b/source/cmk_addons_plugins/meraki/rulesets/appliance_performance.py
new file mode 100644
index 0000000000000000000000000000000000000000..5424f4fcb902ec81c3a254c21066d9017d54a038
--- /dev/null
+++ b/source/cmk_addons_plugins/meraki/rulesets/appliance_performance.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2024-06-23
+# File  : cisco_meraki_org_appliance_performance.py (WATO)
+
+# 2024-06-27: refactored for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki_org_appliance_performance.py in to appliance_performance.py
+from cmk.rulesets.v1 import Help, Title
+from cmk.rulesets.v1.form_specs import (
+    DefaultValue,
+    DictElement,
+    Dictionary,
+    Integer,
+    LevelDirection,
+    SimpleLevels,
+)
+from cmk.rulesets.v1.rule_specs import CheckParameters, HostCondition, Topic
+
+
+def _parameter_form() -> Dictionary:
+    return Dictionary(
+        elements={
+            'levels_upper': DictElement(
+                parameter_form=SimpleLevels(
+                    title=Title("Utilization"),
+                    level_direction=LevelDirection.UPPER,
+                    form_spec_template=Integer(),
+                    prefill_fixed_levels=DefaultValue(value=(60, 80)),
+                    help_text=Help(
+                        'The device utilization data reported to the Meraki'
+                        ' dashboard is based on a load average measured over a'
+                        ' period of one minute. The load value is returned in'
+                        ' numeric values ranging from 1 through 100. A lower'
+                        ' value indicates a lower load, and a higher value'
+                        ' indicates a more intense workload. Currently, the'
+                        ' device utilization value is calculated based upon the'
+                        ' CPU utilization of the MX as well as its traffic load.'
+                        ' If an MX device is consistently over 50% utilization'
+                        ' during normal operation, upgrading to a higher'
+                        ' throughput model or reducing the per-device load'
+                        ' through horizontal scaling should be considered. For'
+                        ' more information see:'
+                        ' https://documentation.meraki.com-MX-Monitoring?and?'
+                        'Reporting-Device?Utiliyation'
+                    ),
+                )
+            )
+        },
+    )
+
+
+rule_spec_cisco_meraki_org_appliance_performance = CheckParameters(
+    name="cisco_meraki_org_appliance_performance",
+    topic=Topic.NETWORKING,
+    parameter_form=_parameter_form,
+    title=Title("Cisco Meraki Appliance Utilization"),
+    condition=HostCondition(),
+)
diff --git a/source/cmk_addons_plugins/meraki/rulesets/appliance_uplinks.py b/source/cmk_addons_plugins/meraki/rulesets/appliance_uplinks.py
new file mode 100644
index 0000000000000000000000000000000000000000..6129d379fbaae39d8043aa5c7a034714cbd2ef90
--- /dev/null
+++ b/source/cmk_addons_plugins/meraki/rulesets/appliance_uplinks.py
@@ -0,0 +1,74 @@
+#!/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-11-05
+# File  : cisco_meraki_org_appliance_uplinks.py (WATO)
+
+# 2024-06-29: refactored for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki_org_appliance_uplinks.py in to appliance_uplinks.py
+
+from cmk.rulesets.v1 import Label, Title, Help
+from cmk.rulesets.v1.form_specs import (
+    DefaultValue,
+    DictElement,
+    Dictionary,
+    FixedValue,
+    ServiceState,
+)
+from cmk.rulesets.v1.rule_specs import CheckParameters, HostAndItemCondition, Topic
+
+
+def _parameter_form() -> Dictionary:
+    return Dictionary(
+        elements={
+            'status_map': DictElement(
+                parameter_form=Dictionary(
+                    title=Title('Map uplink status to monitoring state'),
+                    elements={
+                        "active": DictElement(
+                            parameter_form=ServiceState(
+                                title=Title('Uplink status "active"'),
+                                prefill=DefaultValue(ServiceState.OK)
+                            )),
+                        "ready": DictElement(
+                            parameter_form=ServiceState(
+                                title=Title('Uplink status "ready"'),
+                                prefill=DefaultValue(ServiceState.WARN),
+                            )),
+                        "not_connected": DictElement(
+                            parameter_form=ServiceState(
+                                title=Title('Uplink status "not connected"'),
+                                prefill=DefaultValue(ServiceState.CRIT),
+                            )),
+                        "failed": DictElement(
+                            parameter_form=ServiceState(
+                                title=Title('Uplink status "failed"'),
+                                prefill=DefaultValue(ServiceState.CRIT),
+                            )),
+                    },
+                )),
+            'show_traffic': DictElement(
+                parameter_form=FixedValue(
+                    title=Title('Show bandwidth (use only with cache disabled)'),
+                    help_text=Help(
+                        'Use only with cache disabled in the Meraki special agent settings. '
+                        'The throughput is based on the usage for the last 60 seconds.'
+                    ),
+                    value=True,
+                    label=Label("Bandwidth monitoring enabled")
+                ))
+        },
+    )
+
+
+rule_spec_cisco_meraki_org_appliance_uplinks = CheckParameters(
+    name="cisco_meraki_org_appliance_uplinks",
+    topic=Topic.NETWORKING,
+    parameter_form=_parameter_form,
+    title=Title("Cisco Meraki Appliance uplinks"),
+    condition=HostAndItemCondition(item_title=Title('Uplink name')),
+)
diff --git a/source/cmk_addons_plugins/meraki/rulesets/appliance_vpns.py b/source/cmk_addons_plugins/meraki/rulesets/appliance_vpns.py
new file mode 100644
index 0000000000000000000000000000000000000000..3cc8cca7cce1775aff0f9170839f4def907c3d47
--- /dev/null
+++ b/source/cmk_addons_plugins/meraki/rulesets/appliance_vpns.py
@@ -0,0 +1,42 @@
+#!/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-11-05
+# File  : cisco_meraki_org_appliance_vpns.py (WATO)
+
+# 2024-06-29: refactored for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki_org_appliance_vpns.py in to appliance_vpns.py
+
+from cmk.rulesets.v1 import Title
+from cmk.rulesets.v1.form_specs import (
+    DefaultValue,
+    DictElement,
+    Dictionary,
+    ServiceState,
+)
+from cmk.rulesets.v1.rule_specs import CheckParameters, HostAndItemCondition, Topic
+
+
+def _parameter_form() -> Dictionary:
+    return Dictionary(
+        elements={
+            'status_not_reachable': DictElement(
+                parameter_form=ServiceState(
+                    title=Title('Monitoring state if the VPN peer is not reachable'),
+                    prefill=DefaultValue(ServiceState.WARN),
+                )),
+        }
+    )
+
+
+rule_spec_cisco_meraki_org_appliance_vpns = CheckParameters(
+    name="cisco_meraki_org_appliance_vpns",
+    topic=Topic.NETWORKING,
+    parameter_form=_parameter_form,
+    title=Title("Cisco Meraki Appliance VPNs"),
+    condition=HostAndItemCondition(item_title=Title('VPN peer')),
+)
diff --git a/source/cmk_addons_plugins/meraki/rulesets/licenses_overviewi.py b/source/cmk_addons_plugins/meraki/rulesets/licenses_overviewi.py
new file mode 100644
index 0000000000000000000000000000000000000000..cd207a6d3e175c816a5162c6627f91ecf0ef78d9
--- /dev/null
+++ b/source/cmk_addons_plugins/meraki/rulesets/licenses_overviewi.py
@@ -0,0 +1,100 @@
+#!/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.
+
+# enhancements by thl-cmk[at]outlook[dot]com, https://thl-cmk.hopto.org
+# - changed remaining time (WATO) from Age (Days, Hours, Minutes, Seconds) to Days only
+# - added WATO option for License state is not ok -> default to WARN
+# - added discovery rule for ITEM variant (Org Name/Org ID - this is the default, Org Name, Org ID)
+
+# 2023-11-18: moved discovery rule set to cisco_meraki_organisations for reuse with cisco_meraki_organisations_api
+# 2024-06-28: refactored for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki_org_licenses_overviewi.py in to licenses_overviewi.py
+
+from cmk.rulesets.v1 import Label, Title, Help
+from cmk.rulesets.v1.form_specs import (
+    DefaultValue,
+    DictElement,
+    Dictionary,
+    FixedValue,
+    ServiceState,
+    SimpleLevels,
+    LevelDirection,
+    TimeSpan,
+    TimeMagnitude,
+    validators,
+)
+from cmk.rulesets.v1.rule_specs import CheckParameters, HostAndItemCondition, Topic
+
+_DAY = 60.0 * 60.0 * 24.0
+
+
+def _migrate(p: str | dict) -> dict[str, any]:
+    # change age to days, we suspect there is no warning less than 1 hour in seconds,
+    # and we will not warn/crit above 3600 days (10 years)
+    if p.get('remaining_expiration_time'):
+        warn, crit = p.get('remaining_expiration_time')
+        if warn >= 3600:
+            warn = int(warn / 86400)
+        if crit >= 3600:
+            crit = int(crit / 86400)
+        p['remaining_expiration_time'] = (warn, crit)
+    return p
+
+
+def _parameter_form() -> Dictionary:
+    return Dictionary(
+        elements={
+            "remaining_expiration_time": DictElement(
+                parameter_form=SimpleLevels[float](
+                    title=Title("Remaining licenses expiration time"),
+                    help_text=Help(""),
+                    form_spec_template=TimeSpan(
+                        displayed_magnitudes=[TimeMagnitude.DAY],
+                        custom_validate=(validators.NumberInRange(min_value=0),),
+                    ),
+                    level_direction=LevelDirection.LOWER,
+                    prefill_fixed_levels=DefaultValue((40 * _DAY, 20 * _DAY)),
+                )),
+            # SimpleLevels[int](
+            #     title=Title("Lower levels for remaining expiration time of licenses"),
+            #     level_direction=LevelDirection.LOWER,
+            #     # elements=[
+            #         #     Integer(title=_("Warning at"), unit='Days'),
+            #         #     Integer(title=_("Critical at"), unit='Days'),
+            #         # ],
+            #     )),
+            "state_license_not_ok": DictElement(
+                parameter_form=ServiceState(
+                    title=Title('Monitoring state if License state is not OK'),
+                    prefill=DefaultValue(ServiceState.WARN),
+                )),
+            'dont_show_alias_on_info': DictElement(
+                parameter_form=FixedValue(
+                    value=True,
+                    title=Title('Don\'t show alias on info line'),
+                    label=Label(''),
+                    # help_test=Help(
+                    #     'The alias is the Organisation ID or the Organisation name, depending on the Item.\n'
+                    #     'If the item is the Organisation ID, the alias is the Organisation name and vice versa.\n'
+                    #     'Organisation ID and Organisation name will always shown in the service details.'
+                    # )
+                ))
+        },
+        # migrate=_migrate,
+        # ignored_keys=[
+        #     'internal_item_name',
+        #     'old_item_name',
+        #     'item_variant',
+        # ],
+    )
+
+
+rule_spec_cisco_meraki_org_licenses_overview = CheckParameters(
+    name="cisco_meraki_org_licenses_overview",
+    topic=Topic.NETWORKING,
+    parameter_form=_parameter_form,
+    title=Title("Cisco Meraki Organisation Licenses Overview"),
+    condition=HostAndItemCondition(item_title=Title('Organization')),
+)
diff --git a/source/cmk_addons_plugins/meraki/rulesets/organisations.py b/source/cmk_addons_plugins/meraki/rulesets/organisations.py
new file mode 100644
index 0000000000000000000000000000000000000000..1161b454f2b6cd21e6faf3bed9b556efbb3ef003
--- /dev/null
+++ b/source/cmk_addons_plugins/meraki/rulesets/organisations.py
@@ -0,0 +1,62 @@
+#!/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-11-11
+# File  : cisco_meraki_organisations.py (wato plugin)
+
+# 2023-11-18: split from cisco_meraki_org_licenses_overview.py
+# 2024-06-29: refactored for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki_organisations.py in to organisations.py
+
+from cmk.rulesets.v1 import Help, Title
+from cmk.rulesets.v1.form_specs import (
+    DefaultValue,
+    DictElement,
+    Dictionary,
+    SingleChoice,
+    SingleChoiceElement,
+)
+from cmk.rulesets.v1.rule_specs import DiscoveryParameters, HostCondition, Topic
+
+
+def _discovery_form() -> Dictionary:
+    return Dictionary(
+        elements={
+            'item_variant': DictElement(
+                parameter_form=SingleChoice(
+                    title=Title('Information to use as item'),
+                    help_text=Help(
+                        'You can select how to build the item for this service. By default the Organization ID/name\n'
+                        'is used to stay compatible with the build in check. The information not used for the item\n'
+                        'will be added to the service output.'
+                    ),
+                    elements=[
+                        SingleChoiceElement(
+                            name='org_id',
+                            title=Title('Organization ID')
+                        ),
+                        SingleChoiceElement(
+                            name='org_name',
+                            title=Title('Organization name')
+                        ),
+                        SingleChoiceElement(
+                            name='org_id_name',
+                            title=Title('Organization ID/name')
+                        ),
+                    ],
+                    prefill=DefaultValue('org_id_name')
+                ))
+        }
+    )
+
+
+rule_spec_discovery_meraki_organisations = DiscoveryParameters(
+    name="discovery_meraki_organisations",
+    topic=Topic.GENERAL,
+    parameter_form=_discovery_form,
+    title=Title("Cisco Meraki Organisation (API/Licenses)"),
+)
diff --git a/source/cmk_addons_plugins/meraki/rulesets/organisations_api.py b/source/cmk_addons_plugins/meraki/rulesets/organisations_api.py
new file mode 100644
index 0000000000000000000000000000000000000000..e76cc02d360b1e86fe22f66b1f41342af727644e
--- /dev/null
+++ b/source/cmk_addons_plugins/meraki/rulesets/organisations_api.py
@@ -0,0 +1,62 @@
+#!/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-11-18
+# File  : cisco_meraki_organisations_api.py (wato plugin)
+
+# 2024-06-29: refactored for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki_organisations_api.py in to organisations_api.py
+#             moved ruleset from "Networking" to "Applications, Processes & Services"
+
+from cmk.rulesets.v1 import Title
+from cmk.rulesets.v1.form_specs import (
+    DefaultValue,
+    DictElement,
+    Dictionary,
+    ServiceState,
+    String,
+)
+from cmk.rulesets.v1.rule_specs import CheckParameters, HostAndItemCondition, Topic
+
+
+def _parameter_form() -> Dictionary:
+    return Dictionary(
+        elements={
+            "state_api_not_enabled": DictElement(
+             parameter_form=ServiceState(
+                 title=Title('Monitoring state if API is not enabled'),
+                 prefill=DefaultValue(ServiceState.WARN),
+             )),
+
+            # params from discovery
+            'internal_item_name': DictElement(
+                render_only=True,
+                parameter_form=ServiceState(
+                    title=Title('Discovery internal item name')
+                )),
+            'item_variant': DictElement(
+                render_only=True,
+                parameter_form=ServiceState(
+                    title=Title('Discovery item variant')
+                ))
+        },
+        # ignored_keys=[
+        #     'internal_item_name',
+        #     'old_item_name',
+        #     'item_variant',
+        # ],
+    )
+
+
+rule_spec_cisco_meraki_organisations_api = CheckParameters(
+    name="cisco_meraki_organisations_api",
+    topic=Topic.APPLICATIONS,
+    parameter_form=_parameter_form,
+    title=Title("Cisco Meraki Organisation API"),
+    condition=HostAndItemCondition(item_title=Title('Organization')),
+)
+
diff --git a/source/cmk_addons_plugins/meraki/rulesets/switch_ports_statuses.py b/source/cmk_addons_plugins/meraki/rulesets/switch_ports_statuses.py
new file mode 100644
index 0000000000000000000000000000000000000000..3cea6f758399ab1aae5ec9efe18d8f6ed2f252f3
--- /dev/null
+++ b/source/cmk_addons_plugins/meraki/rulesets/switch_ports_statuses.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2024-02-02
+# File  : cisco_meraki_switch_ports_statuses.py (WATO)
+
+# 2024-05-12: added support for MerakiGetOrganizationSwitchPortsStatusesBySwitch (Early Access)
+#             added traffic counters as perfdata
+# 2024-05-19: reworked switch port traffic
+# 2024-05-20: added discovery rule for port status
+# 2024-06-27: refactored for CMK 2.3
+# 2024-06-30: renamed from cisco_meraki_switch_ports_statuses.py in to switch_ports_statuses.py
+#             added params from discovery as render only
+
+from cmk.rulesets.v1 import Label, Title, Help
+from cmk.rulesets.v1.form_specs import (
+    DefaultValue,
+    DictElement,
+    Dictionary,
+    FixedValue,
+    ServiceState,
+    MultipleChoice,
+    MultipleChoiceElement,
+    String,
+)
+from cmk.rulesets.v1.rule_specs import CheckParameters, DiscoveryParameters, HostAndItemCondition, Topic
+
+from cmk_addons.plugins.meraki.lib.utils import (
+    _SEC_NAME_ORG_SWITCH_PORTS_STATUSES,
+    _SEC_NAME_SWITCH_PORTS_STATUSES,
+)
+
+
+def _parameter_form():
+    return Dictionary(
+        elements={
+            'state_disabled': DictElement(
+                parameter_form=ServiceState(
+                    title=Title('Monitoring state if port is "disabled"'),
+                    prefill=DefaultValue(ServiceState.OK),
+                )),
+            'state_not_connected': DictElement(
+                parameter_form=ServiceState(
+                    title=Title('Monitoring state if port is "not connected"'),
+                    prefill=DefaultValue(ServiceState.OK),
+                )),
+            'state_not_full_duplex': DictElement(
+                parameter_form=ServiceState(
+                    title=Title('Monitoring state if port is "not full duplex"'),
+                    prefill=DefaultValue(ServiceState.WARN),
+                )),
+            'state_speed_change': DictElement(
+                parameter_form=ServiceState(
+                    title=Title('Monitoring state if speed is changed'),
+                    prefill=DefaultValue(ServiceState.WARN),
+                )),
+            'state_admin_change': DictElement(
+                parameter_form=ServiceState(
+                    title=Title('Monitoring state if admin state is changed'),
+                    prefill=DefaultValue(ServiceState.WARN),
+                )),
+            'state_op_change': DictElement(
+                parameter_form=ServiceState(
+                    title=Title('Monitoring state if operational state is changed'),
+                    prefill=DefaultValue(ServiceState.WARN),
+                )),
+            'show_traffic': DictElement(
+                parameter_form=FixedValue(
+                    value=True,
+                    title=Title('Show bandwidth (use only with cache disabled)'),
+                    label=Label('Bandwidth monitoring enabled'),
+                    help_text=Help(
+                        'Use only with cache disabled in the Meraki special agent settings. '
+                        'Depending on your Meraki organization size (in terms of number of switches) '
+                        'this will exceeds the limits of the allowed API requests per second. You can try to '
+                        'enable "Early Access" in the Meraki dashboard. In the Meraki special agent settings '
+                        f'switch from "{_SEC_NAME_SWITCH_PORTS_STATUSES}" to "{_SEC_NAME_ORG_SWITCH_PORTS_STATUSES}". '
+                        'This will fetch all the switch data with one API request instead of one request for each '
+                        'switch.'
+                    ),
+                )),
+            # params from discovery
+            'enabled': DictElement(
+                render_only=True,
+                parameter_form=String(
+                    title=Title('Discovered admin state')
+                )),
+            'status': DictElement(
+                render_only=True,
+                parameter_form=String(
+                    title=Title('Discovered status')
+                )
+            ),
+            'speed': DictElement(
+                render_only=True,
+                parameter_form=String(
+                    title=Title('Discovered speed')
+                )
+            )
+        },
+    )
+
+
+rule_spec_cisco_meraki_switch_ports_statuses = CheckParameters(
+    name="cisco_meraki_switch_ports_statuses",
+    topic=Topic.NETWORKING,
+    parameter_form=_parameter_form,
+    title=Title("Cisco Meraki Switch Ports"),
+    condition=HostAndItemCondition(item_title=Title('Port ID')),
+)
+
+
+def _discovery_form():
+    return Dictionary(
+        elements={
+            'discovered_port_states': DictElement(
+                parameter_form=MultipleChoice(
+                    title=Title('Select Ports to discover'),
+                    elements=[
+                        MultipleChoiceElement(
+                            title=Title('Admin enabled'),
+                            name='admin_enabled',
+                        ),
+                        MultipleChoiceElement(
+                            title=Title('Admin disabled'),
+                            name='admin_disabled',
+                        ),
+                        MultipleChoiceElement(
+                            title=Title('Connected'),
+                            name='connected',
+                        ),
+                        MultipleChoiceElement(
+                            title=Title('Disconnected'),
+                            name='disconnected',
+                        ),
+                    ],
+                    help_text=Help('Select the port states for discovery'),
+                    prefill=DefaultValue([
+                        'admin_enabled',
+                        'admin_disabled',
+                        'connected',
+                        'disconnected',
+                    ])
+                )),
+        },
+    )
+
+
+rule_spec_cisco_meraki_switch_ports_statuses_discovery = DiscoveryParameters(
+    name="discovery_cisco_meraki_switch_ports_statuses",
+    topic=Topic.GENERAL,
+    parameter_form=_discovery_form,
+    title=Title("Cisco Meraki Switch Ports"),
+)
diff --git a/source/cmk_addons_plugins/meraki/rulesets/wireless_device_ssid_status.py b/source/cmk_addons_plugins/meraki/rulesets/wireless_device_ssid_status.py
new file mode 100644
index 0000000000000000000000000000000000000000..8f0d1705719098e80c4bc877e395c3f5f4e59cea
--- /dev/null
+++ b/source/cmk_addons_plugins/meraki/rulesets/wireless_device_ssid_status.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2024-02-02
+# File  : cisco_meraki_org_wireless_status.py (WATO)
+
+# 2024-06-29: refactored for CMK 2.3
+# 2024-06-30 renamed from cisco_meraki_org_wireless_device_status.py in to wireless_device_ssid_status.py
+#            added SSI to the title
+
+from cmk.rulesets.v1 import Title
+from cmk.rulesets.v1.form_specs import (
+    DefaultValue,
+    DictElement,
+    Dictionary,
+    ServiceState,
+)
+from cmk.rulesets.v1.rule_specs import CheckParameters, HostAndItemCondition, Topic
+
+
+def _parameter_form() -> Dictionary:
+    return Dictionary(
+        elements={
+            'state_if_not_enabled': DictElement(
+                parameter_form=ServiceState(
+                    title=Title('Monitoring state if SSID is "not enabled"'),
+                    prefill=DefaultValue(ServiceState.WARN),
+                )),
+        },
+    )
+
+
+rule_spec_cisco_meraki_wireless_device_status = CheckParameters(
+    name="cisco_meraki_wireless_device_status",
+    topic=Topic.NETWORKING,
+    parameter_form=_parameter_form,
+    title=Title("Cisco Meraki Wireless device SSID"),
+    condition=HostAndItemCondition(item_title=Title("SSID")),
+)
diff --git a/source/cmk_plugins/cisco/rulesets/meraki.py b/source/cmk_plugins/cisco/rulesets/meraki.py
new file mode 100644
index 0000000000000000000000000000000000000000..95fc8b3f2d3961322e5c7e9aec2112b3f0df01d4
--- /dev/null
+++ b/source/cmk_plugins/cisco/rulesets/meraki.py
@@ -0,0 +1,272 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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.
+
+#
+# file path must be exactly as the file it shadows :-(
+# local/lip/python3/cmk/plugins/cisco/rulesets
+
+
+from collections.abc import Iterable, Sequence
+
+from cmk.rulesets.v1 import Help, Label, Message, Title
+from cmk.rulesets.v1.form_specs import (
+    # BooleanChoice,
+    DefaultValue,
+    DictElement,
+    Dictionary,
+    FixedValue,
+    Integer,
+    List,
+    MultipleChoice,
+    MultipleChoiceElement,
+    Password,
+    Proxy,
+    String,
+    migrate_to_password,
+    migrate_to_proxy,
+)
+
+from cmk.rulesets.v1.form_specs.validators import ValidationError, NumberInRange
+from cmk.rulesets.v1.rule_specs import SpecialAgent, Topic
+from cmk_addons.plugins.meraki.lib.utils import (
+    _SEC_CACHE_APPLIANCE_PERFORMANCE,
+    _SEC_CACHE_APPLIANCE_UPLINKS_USAGE,
+    _SEC_CACHE_APPLIANCE_UPLINKS,
+    _SEC_CACHE_APPLIANCE_VPNS,
+    _SEC_CACHE_CELLULAR_UPLINKS,
+    _SEC_CACHE_DEVICE_INFO,
+    _SEC_CACHE_DEVICE_STATUSES,
+    _SEC_CACHE_DEVICE_UPLINKS_INFO,
+    _SEC_CACHE_LICENSES_OVERVIEW,
+    _SEC_CACHE_NETWORKS,
+    _SEC_CACHE_ORG_API_REQUESTS,
+    _SEC_CACHE_ORG_SWITCH_PORTS_STATUSES,
+    _SEC_CACHE_ORGANISATIONS,
+    _SEC_CACHE_SENSOR_READINGS,
+    _SEC_CACHE_SWITCH_PORTS_STATUSES,
+    _SEC_CACHE_WIRELESS_DEVICE_STATUS,
+    _SEC_CACHE_WIRELESS_ETHERNET_STATUSES,
+)
+
+_SEC_NAME_APPLIANCE_UPLINKS = "appliance_uplinks"
+_SEC_NAME_APPLIANCE_UPLINKS_USAGE = "appliance_uplinks_usage"
+_SEC_NAME_APPLIANCE_VPNS = "appliance_vpns"
+_SEC_NAME_APPLIANCE_PERFORMANCE = "appliance_performance"
+_SEC_NAME_CELLULAR_UPLINKS = "cellular_uplinks"
+_SEC_NAME_DEVICE_INFO = "device_info"
+_SEC_NAME_DEVICE_STATUSES = "device_status"
+_SEC_NAME_DEVICE_UPLINKS_INFO = "device_uplinks_info"
+_SEC_NAME_LICENSES_OVERVIEW = "licenses_overview"
+_SEC_NAME_NETWORKS = "networks"
+_SEC_NAME_ORGANISATIONS = "organisations"
+_SEC_NAME_ORG_API_REQUESTS = "api_requests_by_organization"
+_SEC_NAME_SENSOR_READINGS = "sensor_readings"
+_SEC_NAME_SWITCH_PORTS_STATUSES = "switch_ports_statuses"
+_SEC_NAME_WIRELESS_DEVICE_STATUS = "wireless_device_status"
+_SEC_NAME_WIRELESS_ETHERNET_STATUSES = "wireless_ethernet_statuses"
+_SEC_NAME_ORG_SWITCH_PORTS_STATUSES = "org_switch_ports_statuses"
+
+_SEC_TITLE_DEVICE_INFO = 'Device info (Organization)'
+_SEC_TITLE_NETWORKS = 'Network info (Organization)'
+_SEC_TITLE_ORGANISATIONS = 'Organization (Agent)'
+_SEC_TITLE_ORG_API_REQUESTS = 'API request (Organizaion)'
+_SEC_TITLE_APPLIANCE_UPLINKS = 'Appliances uplinks (Organizaion)'
+_SEC_TITLE_APPLIANCE_UPLINKS_USAGE = 'Appliances uplinks usage (Organizaion)'
+_SEC_TITLE_APPLIANCE_VPNS = 'Appliances VPNs (Organizaion)'
+_SEC_TITLE_APPLIANCE_PERFORMANCE = 'Appliances Utilization (Device)'
+_SEC_TITLE_CELLULAR_UPLINKS = 'Cellular devices uplinks (Organizaion)'
+_SEC_TITLE_DEVICE_STATUSES = 'Devices status (Organizaion)'
+_SEC_TITLE_DEVICE_UPLINKS_INFO = 'Devices uplink info (Organizaion)'
+_SEC_TITLE_LICENSES_OVERVIEW = 'Licenses overview (Organizaion)'
+_SEC_TITLE_SENSOR_READINGS = 'Sensors readings (Organizaion)'
+_SEC_TITLE_SWITCH_PORTS_STATUSES = 'Switch ports status (Device)'
+_SEC_TITLE_WIRELESS_ETHERNET_STATUSES = 'Wireless devices ethernet status (Organizaion)'
+_SEC_TITLE_WIRELESS_DEVICE_STATUS = 'Wireless devices SSIDs status (Device)'
+_SEC_TITLE_ORG_SWITCH_PORTS_STATUSES = 'Switch port status (Organizaion/Early Access)'
+
+
+class DuplicateInList:  # pylint: disable=too-few-public-methods
+    """ Custom validator that ensures the validated list has no duplicate entries. """
+
+    def __init__(
+            self,
+    ) -> None:
+        pass
+
+    @staticmethod
+    def _get_default_errmsg(_duplicates: Sequence) -> Message:
+        return Message(f"Duplicate element in list. Duplicate elements: {', '.join(_duplicates)}")
+
+    def __call__(self, value: List[str] | None) -> None:
+        if not isinstance(value, list):
+            return
+        _duplicates = [value[i] for i, x in enumerate(value) if value.count(x) > 1]
+        _duplicates = list(set(_duplicates))
+        if _duplicates:
+            raise ValidationError(message=self._get_default_errmsg(_duplicates))
+
+
+def _migrate_to_valid_ident(value: object) -> Sequence[str]:
+    if not isinstance(value, Iterable):
+        raise ValueError("Invalid value {value} for sections")
+
+    name_mapping = {
+        "licenses-overview": "licenses_overview",
+        "device-statuses": "device_statuses",
+        "sensor-readings": "sensor_readings",
+    }
+
+    # return [name_mapping.get(s, s) for s in value]
+    return [s.replace('-', '_') for s in value]
+
+
+def _form_special_agent_cisco_meraki() -> Dictionary:
+    return Dictionary(
+        title=Title("Cisco Meraki"),
+        elements={
+            "api_key": DictElement(
+                parameter_form=Password(
+                    title=Title("API Key"),
+                    migrate=migrate_to_password
+                ),
+                required=True,
+            ),
+            "proxy": DictElement(
+                parameter_form=Proxy(
+                    migrate=migrate_to_proxy
+                )
+            ),
+            "no_cache": DictElement(
+                parameter_form=FixedValue(  # BooleanChoice needs 2 clicks :-(
+                    title=Title("Disable Cache"),
+                    help_text=Help(
+                        "Never use cached information. By default the agent will cache received "
+                        "data to avoid API limits and speed up the data retrievel."
+                    ),
+                    label=Label("API Cache is disabled"),
+                    value=True,
+                )
+            ),
+            'org_id_as_prefix': DictElement(
+                parameter_form=FixedValue(
+                    value=True,
+                    title=Title('Uese organisation ID as host prefix'),
+                    label=Label("The Organization-id will be used as host name prefix"),
+                    help_text=Help(
+                        'The organisation ID will be used as prefix for the hostname (separated by a "\'"). Use '
+                        'this option together with a "Hostname translation for piggybacked hosts" to add a '
+                        'organisation prefix to the hosts from the Cisco Meraki cloud to avoid conflicting '
+                        'hostnames. You can also use this option along with the "Dynamic host management" to '
+                        'sort the host in organisation specific folders.'
+                    )
+                )),
+
+        "excluded_sections": DictElement(
+                parameter_form=MultipleChoice(
+                    title=Title("Exclude Sections"),
+                    elements=[
+                        MultipleChoiceElement(name=_SEC_NAME_ORG_API_REQUESTS,
+                                              title=Title(_SEC_TITLE_ORG_API_REQUESTS)),
+                        MultipleChoiceElement(name=_SEC_NAME_APPLIANCE_UPLINKS,
+                                              title=Title(_SEC_TITLE_APPLIANCE_UPLINKS)),
+                        MultipleChoiceElement(name=_SEC_NAME_APPLIANCE_UPLINKS_USAGE,
+                                              title=Title(_SEC_TITLE_APPLIANCE_UPLINKS_USAGE)),
+                        MultipleChoiceElement(name=_SEC_NAME_APPLIANCE_VPNS, title=Title(_SEC_TITLE_APPLIANCE_VPNS)),
+                        MultipleChoiceElement(name=_SEC_NAME_APPLIANCE_PERFORMANCE,
+                                              title=Title(_SEC_TITLE_APPLIANCE_PERFORMANCE)),
+                        MultipleChoiceElement(name=_SEC_NAME_CELLULAR_UPLINKS,
+                                              title=Title(_SEC_TITLE_CELLULAR_UPLINKS)),
+                        MultipleChoiceElement(name=_SEC_NAME_DEVICE_STATUSES, title=Title(_SEC_TITLE_DEVICE_STATUSES)),
+                        MultipleChoiceElement(name=_SEC_NAME_DEVICE_UPLINKS_INFO,
+                                              title=Title(_SEC_TITLE_DEVICE_UPLINKS_INFO)),
+                        MultipleChoiceElement(name=_SEC_NAME_LICENSES_OVERVIEW,
+                                              title=Title(_SEC_TITLE_LICENSES_OVERVIEW)),
+                        MultipleChoiceElement(name=_SEC_NAME_SENSOR_READINGS, title=Title(_SEC_TITLE_SENSOR_READINGS)),
+                        MultipleChoiceElement(name=_SEC_NAME_SWITCH_PORTS_STATUSES,
+                                              title=Title(_SEC_TITLE_SWITCH_PORTS_STATUSES)),
+                        MultipleChoiceElement(name=_SEC_NAME_WIRELESS_ETHERNET_STATUSES,
+                                              title=Title(_SEC_TITLE_WIRELESS_ETHERNET_STATUSES)),
+                        MultipleChoiceElement(name=_SEC_NAME_WIRELESS_DEVICE_STATUS,
+                                              title=Title(_SEC_TITLE_WIRELESS_DEVICE_STATUS)),
+                        MultipleChoiceElement(name=_SEC_NAME_ORG_SWITCH_PORTS_STATUSES,
+                                              title=Title(_SEC_TITLE_ORG_SWITCH_PORTS_STATUSES)),
+                    ],
+                    prefill=DefaultValue([
+                        _SEC_NAME_APPLIANCE_PERFORMANCE,
+                        _SEC_NAME_SWITCH_PORTS_STATUSES,
+                        _SEC_NAME_WIRELESS_DEVICE_STATUS,
+                        _SEC_NAME_ORG_SWITCH_PORTS_STATUSES,
+                    ]),
+                    # migrate=_migrate_to_valid_ident,
+                ),
+                required=True,
+            ),
+            "orgs": DictElement(
+                parameter_form=List(
+                    element_template=String(macro_support=True), title=Title("Organizations"),
+                    custom_validate=(DuplicateInList(),),
+                ),
+            ),
+            "cache_per_section": DictElement(
+                parameter_form=Dictionary(
+                    title=Title("Set Cache time per section"),
+                    elements={
+                        sec_name: DictElement(
+                            parameter_form=Integer(
+                                title=Title(sec_title),
+                                prefill=DefaultValue(sec_cache),
+                                unit_symbol="minutes",
+                                custom_validate=(NumberInRange(min_value=0),)
+                            )
+                        ) for sec_name, sec_title, sec_cache in [
+                            (_SEC_NAME_APPLIANCE_PERFORMANCE, _SEC_TITLE_APPLIANCE_PERFORMANCE, _SEC_CACHE_APPLIANCE_PERFORMANCE),
+                            (_SEC_NAME_APPLIANCE_UPLINKS_USAGE, _SEC_TITLE_APPLIANCE_UPLINKS_USAGE, _SEC_CACHE_APPLIANCE_UPLINKS_USAGE),
+                            (_SEC_NAME_APPLIANCE_UPLINKS, _SEC_TITLE_APPLIANCE_UPLINKS, _SEC_CACHE_APPLIANCE_UPLINKS),
+                            (_SEC_NAME_APPLIANCE_VPNS, _SEC_TITLE_APPLIANCE_VPNS, _SEC_CACHE_APPLIANCE_VPNS),
+                            (_SEC_NAME_CELLULAR_UPLINKS, _SEC_TITLE_CELLULAR_UPLINKS, _SEC_CACHE_CELLULAR_UPLINKS),
+                            (_SEC_NAME_DEVICE_INFO, _SEC_TITLE_DEVICE_INFO, _SEC_CACHE_DEVICE_INFO),
+                            (_SEC_NAME_DEVICE_STATUSES, _SEC_TITLE_DEVICE_STATUSES, _SEC_CACHE_DEVICE_STATUSES),
+                            (_SEC_NAME_DEVICE_UPLINKS_INFO, _SEC_TITLE_DEVICE_UPLINKS_INFO, _SEC_CACHE_DEVICE_UPLINKS_INFO),
+                            (_SEC_NAME_LICENSES_OVERVIEW, _SEC_TITLE_LICENSES_OVERVIEW, _SEC_CACHE_LICENSES_OVERVIEW),
+                            (_SEC_NAME_NETWORKS, _SEC_TITLE_NETWORKS, _SEC_CACHE_NETWORKS),
+                            (_SEC_NAME_ORG_API_REQUESTS, _SEC_TITLE_ORG_API_REQUESTS, _SEC_CACHE_ORG_API_REQUESTS),
+                            (_SEC_NAME_ORG_SWITCH_PORTS_STATUSES, _SEC_TITLE_ORG_SWITCH_PORTS_STATUSES, _SEC_CACHE_ORG_SWITCH_PORTS_STATUSES),
+                            (_SEC_NAME_ORGANISATIONS, _SEC_TITLE_ORGANISATIONS, _SEC_CACHE_ORGANISATIONS),
+                            (_SEC_NAME_SENSOR_READINGS, _SEC_TITLE_SENSOR_READINGS, _SEC_CACHE_SENSOR_READINGS),
+                            (_SEC_NAME_SWITCH_PORTS_STATUSES, _SEC_TITLE_SWITCH_PORTS_STATUSES, _SEC_CACHE_SWITCH_PORTS_STATUSES),
+                            (_SEC_NAME_WIRELESS_DEVICE_STATUS, _SEC_TITLE_WIRELESS_DEVICE_STATUS, _SEC_CACHE_WIRELESS_DEVICE_STATUS),
+                            (_SEC_NAME_WIRELESS_ETHERNET_STATUSES, _SEC_TITLE_WIRELESS_ETHERNET_STATUSES, _SEC_CACHE_WIRELESS_ETHERNET_STATUSES),
+                        ]
+                    }
+                )
+            ),
+            "sections": DictElement(
+                parameter_form=MultipleChoice(
+                    title=Title("Sections"),
+                    elements=[
+                        MultipleChoiceElement(
+                            name="licenses_overview", title=Title("Organization licenses overview")
+                        ),
+                        MultipleChoiceElement(
+                            name="device_statuses", title=Title("Organization device statuses")
+                        ),
+                        MultipleChoiceElement(
+                            name="sensor_readings", title=Title("Organization sensor readings")
+                        ),
+                    ],
+                    # migrate=_migrate_to_valid_ident,
+                ),
+                render_only=True,
+            ),
+        },
+    )
+
+
+rule_spec_cisco_meraki = SpecialAgent(
+    name="cisco_meraki",
+    title=Title("Cisco Meraki"),
+    topic=Topic.APPLICATIONS,
+    parameter_form=_form_special_agent_cisco_meraki,
+)
diff --git a/source/agents/special/agent_cisco_meraki b/source/cmk_plugins/collection/libexec/agent_cisco_meraki
similarity index 80%
rename from source/agents/special/agent_cisco_meraki
rename to source/cmk_plugins/collection/libexec/agent_cisco_meraki
index d4ddb888718631c6ff30bfe5d5a93feb362eef69..4f569d1b0075b74bfe5c38fec74c14b0d76034a1 100755
--- a/source/agents/special/agent_cisco_meraki
+++ b/source/cmk_plugins/collection/libexec/agent_cisco_meraki
@@ -5,7 +5,7 @@
 
 import sys
 
-from cmk.special_agents.agent_cisco_meraki import main
+from cmk_addons.plugins.meraki.lib import agent
 
 if __name__ == "__main__":
-    sys.exit(main())
+    sys.exit(agent.main())
diff --git a/source/cmk_plugins/collection/server_side_calls/cisco_meraki.py b/source/cmk_plugins/collection/server_side_calls/cisco_meraki.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f772f38eb0cbe25f75362eb1e7ab1e80654766c
--- /dev/null
+++ b/source/cmk_plugins/collection/server_side_calls/cisco_meraki.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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 collections.abc import Iterator, Sequence, Mapping
+
+from pydantic import BaseModel
+
+from cmk.server_side_calls.v1 import (
+    EnvProxy,
+    HostConfig,
+    NoProxy,
+    replace_macros,
+    Secret,
+    SpecialAgentCommand,
+    SpecialAgentConfig,
+    URLProxy,
+)
+
+__param = {
+    'api_key': Secret(
+        id=139915660185968,
+        format='%s',
+        pass_safely=True
+    ),
+    'no_cache': True,
+    'org_id_as_prefix': True,
+    'excluded_sections': [
+        'appliance_performance',
+        'switch_ports_statuses',
+        'wireless_device_status',
+        'org_switch_ports_statuses'
+    ],
+    'orgs': ['1234', '670771'],
+    'cache_per_section': {
+        'appliance_performance': 0,
+        'appliance_uplinks_usage': 0,
+        'appliance_uplinks': 60,
+        'appliance_vpns': 60,
+        'cellular_uplinks': 60,
+        'device_status': 60,
+        'device_uplinks_info': 60,
+        'licenses_overview': 600,
+        'networks': 600,
+        'api_requests_by_organization': 0,
+        'org_switch_ports_statuses': 0,
+        'organisations': 600,
+        'sensor_readings': 0,
+        'switch_ports_statuses': 0,
+        'wireless_device_status': 30,
+        'wireless_ethernet_statuses': 30
+    }
+}
+
+
+class CachePerSection(BaseModel):
+    appliance_performance: int | None = None
+    appliance_uplinks_usage: int | None = None
+    appliance_uplinks: int | None = None
+    appliance_vpns: int | None = None
+    cellular_uplinks: int | None = None
+    device_info: int | None = None
+    device_status: int | None = None
+    device_uplinks_info: int | None = None
+    licenses_overview: int | None = None
+    networks: int | None = None
+    api_requests_by_organization: int | None = None
+    org_switch_ports_statuses: int | None = None
+    organisations: int | None = None
+    sensor_readings: int | None = None
+    switch_ports_statuses: int | None = None
+    wireless_device_status: int | None = None
+    wireless_ethernet_statuses: int | None = None
+
+
+class Params(BaseModel):
+    api_key: Secret
+    proxy: URLProxy | NoProxy | EnvProxy | None = None
+    sections: Sequence[str] | None = None
+    orgs: Sequence[str] | None = None
+    excluded_sections: Sequence[str] | None = None
+    org_id_as_prefix: bool | None = None
+    no_cache: bool | None = None
+    cache_per_section: CachePerSection | None = None
+
+
+def _agent_cisco_meraki_parser(params: Mapping[str, object]) -> Params:
+    # if 'api-requests-by-organization' in params.get('excluded_sections', []):
+    # print(params)
+    return Params.model_validate(params)
+
+
+def agent_cisco_meraki_arguments(
+        params: Params,
+        host_config: HostConfig,
+) -> Iterator[SpecialAgentCommand]:
+    # print(params)
+
+    args: list[str | Secret] = [
+        host_config.name,
+        params.api_key.unsafe(),
+    ]
+
+    match params.proxy:
+        case URLProxy(url=url):
+            args += ["--proxy", url]
+        case EnvProxy():
+            args += ["--proxy", "FROM_ENVIRONMENT"]
+        case NoProxy():
+            args += ["--proxy", "NO_PROXY"]
+
+    if params.sections is not None:
+        args.append("--sections")
+        args += [s.replace("_", "-") for s in params.sections]
+
+    if params.excluded_sections is not None:
+        args.append("--excluded-sections")
+        args += [s.replace("_", "-") for s in params.excluded_sections]
+
+    if params.orgs is not None:
+        args.append("--orgs")
+        args += [replace_macros(org, host_config.macros) for org in params.orgs]
+
+    if params.cache_per_section is not None:
+        args.append("--cache-per-section")
+        args += [
+            str(cache_value) if cache_value is not None else str(default_cache) for
+            cache_value, default_cache in [
+                (params.cache_per_section.appliance_performance, 0),
+                (params.cache_per_section.appliance_uplinks_usage, 0),
+                (params.cache_per_section.appliance_uplinks, 60),
+                (params.cache_per_section.appliance_vpns, 60),
+                (params.cache_per_section.cellular_uplinks, 60),
+                (params.cache_per_section.device_info, 60),
+                (params.cache_per_section.device_status, 60),
+                (params.cache_per_section.device_uplinks_info, 60),
+                (params.cache_per_section.licenses_overview, 600),
+                (params.cache_per_section.networks, 600),
+                (params.cache_per_section.api_requests_by_organization, 0),
+                (params.cache_per_section.org_switch_ports_statuses, 0),
+                (params.cache_per_section.organisations, 600),
+                (params.cache_per_section.sensor_readings, 0),
+                (params.cache_per_section.switch_ports_statuses, 0),
+                (params.cache_per_section.wireless_device_status, 30),
+                (params.cache_per_section.wireless_ethernet_statuses, 30),
+            ]
+        ]
+    #  default=[0, 0, 60, 60, 60, 60, 60, 60, 600, 600, 0, 0, 600, 0, 0, 30, 30]
+    # print(args)
+
+    if params.org_id_as_prefix is True:
+        args.append("--org-id-as-prefix")
+
+    if params.no_cache is True:
+        args.append("--no-cache")
+
+    yield SpecialAgentCommand(command_arguments=args)
+
+
+special_agent_cisco_meraki = SpecialAgentConfig(
+    name="cisco_meraki",
+    # parameter_parser=Params.model_validate,
+    parameter_parser=_agent_cisco_meraki_parser,
+    commands_function=agent_cisco_meraki_arguments,
+)
diff --git a/source/gui/metrics/cisco_meraki.py b/source/gui/metrics/cisco_meraki.py
deleted file mode 100644
index 75e896cba96487053d43dad8125dc19085a9fb18..0000000000000000000000000000000000000000
--- a/source/gui/metrics/cisco_meraki.py
+++ /dev/null
@@ -1,332 +0,0 @@
-#!/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-11-04
-# File  : cisco_meraki.py (metrics)
-#
-# 2023-11-12: added wireless device status (channel, channel width, signal power)
-# 2024-05-12: added switch port statuses and API return Codes
-# 2024-06-24: fixed, wrong total dor SSID perfometer signal_power -> 30
-
-from cmk.gui.i18n import _
-
-from cmk.gui.plugins.metrics.utils import (
-    check_metrics,
-    graph_info,
-    metric_info,
-    perfometer_info
-)
-
-#
-# license overview
-#
-metric_info['sum_licensed_devices'] = {
-    'title': _('Licensed devices'),
-    'unit': 'count',
-    'color': '31/a',
-}
-
-metric_info['remaining_time'] = {
-    'title': _('Remaining time'),
-    'unit': 's',
-    'color': '26/a',
-}
-
-graph_info['cisco_meraki.remaining_time'] = {
-    'title': _('Cisco Meraki Licenses remaining time'),
-    'metrics': [
-        ('remaining_time', 'area'),
-    ],
-    'scalars': [
-        ('remaining_time:crit', _('crit')),
-        ('remaining_time:warn', _('warn')),
-    ],
-    'range': (0, 'remaining_time:max'),
-}
-
-graph_info['cisco_meraki.licensed_devices'] = {
-    'title': _('Cisco Meraki Licensed devices'),
-    'metrics': [
-        ('sum_licensed_devices', 'area'),
-    ],
-    'range': (0, 'sum_licensed_devices:max'),
-}
-
-perfometer_info.append(('stacked', [
-    {
-        'type': 'logarithmic',
-        'metric': 'remaining_time',
-        # 'half_value': 2592000.0,  # ome month
-        'half_value': 31104000.0,  # ome year
-        'exponent': 2,
-    },
-    {
-        'type': 'logarithmic',
-        'metric': 'sum_licensed_devices',
-        'half_value': 500.0,
-        'exponent': 2,
-    }
-]))
-
-#
-# device_status
-#
-metric_info['last_reported'] = {
-    'title': _('Last reported'),
-    'unit': 's',
-    'color': '26/a',
-}
-
-graph_info['cisco_meraki.device_status'] = {
-    'title': _('Cisco Meraki device status'),
-    'metrics': [
-        ('last_reported', 'area'),
-    ],
-    'range': (0, 'last_reported:max'),
-    'scalars': [
-        ('last_reported:crit', _('crit')),
-        ('last_reported:warn', _('warn')),
-    ],
-}
-
-perfometer_info.append({
-    'type': 'logarithmic',
-    'metric': 'last_reported',
-    'half_value': 7200,  # 2 hours
-    'exponent': 2,
-})
-
-#
-# wireless devices status
-#
-metric_info["signal_power"] = {
-    "title": _("Power"),
-    "unit": "dbm",
-    "color": "#20c080",
-}
-
-metric_info["channel_width"] = {
-    "title": _("Channel Width"),
-    "unit": "hz",
-    "color": "11/a",
-}
-
-metric_info["channel"] = {
-    "title": _("Channel"),
-    "unit": "count",
-    "color": "21/a",
-}
-
-graph_info['cisco_meraki.wireless_device_status.signal_power'] = {
-    'title': _('Signal power'),
-    'metrics': [
-        ('signal_power', 'area'),
-    ],
-    'range': (0, 'signal_power:max'),
-}
-
-graph_info['cisco_meraki.wireless_device_status.channel_width'] = {
-    'title': _('Channel Width'),
-    'metrics': [
-        ('channel_width', 'area'),
-    ],
-    'range': (0, 'channel_width:max'),
-}
-
-graph_info['cisco_meraki.wireless_device_status.channel'] = {
-    'title': _('Channel'),
-    'metrics': [
-        ('channel', 'area'),
-    ],
-    'range': (0, 'channel:max'),
-}
-
-perfometer_info.append({
-    'type': 'linear',
-    'segments': ['signal_power'],
-    'total': 30,
-})
-
-#
-# switch port statuses
-#
-# check_metrics['check_mk-cisco_meraki_organisations_api'] = {
-#     'traffic_total': {'auto_graph': False},
-#     'traffic_sent': {'auto_graph': False},
-#     'traffic_received': {'auto_graph': False},
-# }
-#
-# metric_info["traffic_total"] = {
-#     "title": _("Total bandwidth"),
-#     "unit": "bits/s",
-#     "color": "11/a",
-# }
-#
-# metric_info["traffic_sent"] = {
-#     "title": _("Output bandwidth"),
-#     "unit": "bits/s",
-#     "color": "#0080e0",
-# }
-#
-# metric_info["traffic_received"] = {
-#     "title": _("Input bandwidth"),
-#     "unit": "bits/s",
-#     "color": "#00e060",
-# }
-#
-# graph_info['cisco_meraki.switch_port_status.traffic'] = {
-#     'title': _('Traffic'),
-#     'metrics': [
-#         ('traffic_received', 'area'),
-#         ('traffic_sent', '-area'),
-#     ],
-# }
-# perfometer_info.append(
-#     {
-#         "type": "dual",
-#         "perfometers": [
-#             {
-#                 "type": "logarithmic",
-#                 "metric": "traffic_received",
-#                 "half_value": 500000,
-#                 "exponent": 5,
-#             },
-#             {
-#                 "type": "logarithmic",
-#                 "metric": "traffic_sent",
-#                 "half_value": 500000,
-#                 "exponent": 5,
-#             },
-#         ],
-#     }
-# )
-
-#
-# API return Codes
-#
-check_metrics['check_mk-cisco_meraki_organisations_api'] = {
-    'api_code_2xx': {'auto_graph': False},
-    'api_code_3xx': {'auto_graph': False},
-    'api_code_4xx': {'auto_graph': False},
-    'api_code_5xx': {'auto_graph': False},
-}
-
-metric_info["api_code_2xx"] = {
-    "title": _("Code 2xx"),
-    "unit": "count",
-    "color": "#00e060",
-}
-metric_info["api_code_3xx"] = {
-    "title": _("Code 3xx"),
-    "unit": "count",
-    "color": "#20e060",
-}
-metric_info["api_code_4xx"] = {
-    "title": _("Code 4xx"),
-    "unit": "count",
-    "color": "#0080e0",
-}
-metric_info["api_code_5xx"] = {
-    "title": _("Code 5xx"),
-    "unit": "count",
-    "color": "#2080e0",
-}
-
-
-graph_info['cisco_meraki.cisco_meraki_organisations_api.code'] = {
-    'title': _('Cisco Meraki API response codes'),
-    'metrics': [
-        ('api_code_2xx', 'line'),
-        ('api_code_3xx', 'line'),
-        ('api_code_4xx', '-line'),
-        ('api_code_5xx', '-line'),
-    ],
-    'optional_metrics': [
-        'api_code_2xx',
-        'api_code_3xx',
-        'api_code_4xx',
-        'api_code_5xx',
-    ]
-}
-perfometer_info.append(
-    {
-        "type": "stacked",
-        "perfometers": [
-            {
-                "type": "logarithmic",
-                "metric": "api_code_2xx",
-                "half_value": 100,
-                "exponent": 5,
-            },
-            {
-                "type": "logarithmic",
-                "metric": "api_code_4xx",
-                "half_value": 100,
-                "exponent": 5,
-            },
-        ],
-    }
-)
-perfometer_info.append({
-    "type": "logarithmic",
-    "metric": "api_code_2xx",
-    "half_value": 100,
-    "exponent": 5,
-})
-perfometer_info.append({
-    "type": "logarithmic",
-    "metric": "api_code_4xx",
-    "half_value": 100,
-    "exponent": 5,
-})
-
-# appliance performance/utitlization
-metric_info["utilization"] = {
-    "title": _("Utilization"),
-    "unit": "%",
-    "color": "16/a",
-}
-graph_info['cisco_meraki.cisco_meraki_appliance.utilization'] = {
-    'title': _('Cisco Meraki Appliance Utilization'),
-    'metrics': [
-        ('utilization', 'area'),
-    ],
-    'scalars': [
-        ('utilization:crit', _('crit')),
-        ('utilization:warn', _('warn')),
-    ],
-    'range': (0, 100),
-}
-perfometer_info.append({
-    'type': 'linear',
-    'segments': ['utilization'],
-    'total': 100,
-})
-
-# testing only
-# metric_info["usage_out"] = {
-#     "title": _("Usage Out"),
-#     "unit": "count",
-#     "color": "#0080e0",
-# }
-#
-# metric_info["usage_in"] = {
-#     "title": _("Usage In"),
-#     "unit": "count",
-#     "color": "#00e060",
-# }
-#
-# graph_info['cisco_meraki.switch_port_status.usage'] = {
-#     'title': _('Usage'),
-#     'metrics': [
-#         ('usage_in', 'area'),
-#         ('usage_out', '-area'),
-#     ],
-#     'optional_metrics': [
-#         'usage_in',
-#         'usage_out',
-#     ]
-# }
diff --git a/source/gui/wato/check_parameters/cisco_meraki_org_appliance_performance.py b/source/gui/wato/check_parameters/cisco_meraki_org_appliance_performance.py
deleted file mode 100644
index 715f7f29ce31753af67576ae337e6585920ce183..0000000000000000000000000000000000000000
--- a/source/gui/wato/check_parameters/cisco_meraki_org_appliance_performance.py
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-#
-# License: GNU General Public License v2
-#
-# Author: thl-cmk[at]outlook[dot]com
-# URL   : https://thl-cmk.hopto.org
-# Date  : 2024-06-23
-# File  : cisco_meraki_org_appliance_performance.py (WATO)
-
-from cmk.gui.i18n import _
-from cmk.gui.plugins.wato.utils import (
-    CheckParameterRulespecWithoutItem,
-    rulespec_registry,
-    RulespecGroupCheckParametersNetworking,
-)
-from cmk.gui.valuespec import (
-    Dictionary,
-    Integer,
-    Tuple,
-)
-
-
-def _parameter_valuespec_cisco_meraki_org_appliance_performance():
-    return Dictionary(
-        title=_('Cisco Meraki Appliance Utilization'),
-        optional_keys=True,
-        elements=[
-            ('levels_upper',
-             Tuple(
-                 title=_('Upper Levels'),
-                 elements=[
-                     Integer(
-                         title=_("Warning at"),
-                         unit='%',
-                         default_value=60,
-                         minvalue=0,
-                         maxvalue=101,
-                     ),
-                     Integer(
-                         title=_("Critical at"),
-                         unit='%',
-                         default_value=80,
-                         minvalue=0,
-                         maxvalue=101,
-                     ),
-                 ],
-                 help=_(
-                     'The device utilization data reported to the Meraki'
-                     ' dashboard is based on a load average measured over a'
-                     ' period of one minute. The load value is returned in'
-                     ' numeric values ranging from 1 through 100. A lower'
-                     ' value indicates a lower load, and a higher value'
-                     ' indicates a more intense workload. Currently, the'
-                     ' device utilization value is calculated based upon the'
-                     ' CPU utilization of the MX as well as its traffic load.'
-                     ' If an MX device is consistently over 50% utilization'
-                     ' during normal operation, upgrading to a higher'
-                     ' throughput model or reducing the per-device load'
-                     ' through horizontal scaling should be considered. For'
-                     ' more information see:'
-                     ' https://documentation.meraki.com-MX-Monitoring?and?'
-                     'Reporting-Device?Utiliyation'),
-             )),
-        ],
-    )
-
-
-rulespec_registry.register(
-    CheckParameterRulespecWithoutItem(
-        title=lambda: _('Cisco Meraki Appliance Utilization'),
-        check_group_name='cisco_meraki_org_appliance_performance',
-        group=RulespecGroupCheckParametersNetworking,
-        parameter_valuespec=_parameter_valuespec_cisco_meraki_org_appliance_performance,
-        match_type='dict',
-    )
-)
diff --git a/source/gui/wato/check_parameters/cisco_meraki_org_appliance_uplinks.py b/source/gui/wato/check_parameters/cisco_meraki_org_appliance_uplinks.py
deleted file mode 100644
index f0ef96ca17ec47137dcd3aa225532e45e94f2941..0000000000000000000000000000000000000000
--- a/source/gui/wato/check_parameters/cisco_meraki_org_appliance_uplinks.py
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/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-11-05
-# File  : cisco_meraki_org_appliance_uplinks.py (WATO)
-
-from cmk.gui.i18n import _
-from cmk.gui.plugins.wato.utils import (
-    CheckParameterRulespecWithItem,
-    RulespecGroupCheckParametersNetworking,
-    rulespec_registry,
-)
-from cmk.gui.valuespec import (
-    Dictionary,
-    FixedValue,
-    MonitoringState,
-    TextInput,
-)
-
-
-def _parameter_valuespec_cisco_meraki_org_appliance_uplinks():
-    return Dictionary(
-        title=_('Cisco Meraki Appliance Uplinks'),
-        optional_keys=True,
-        elements=[
-            ('status_map',
-             Dictionary(
-                 title=_('Map uplink status to monitoring state'),
-                 elements=[
-                     ("active",
-                      MonitoringState(
-                          title=_('Monitoring state for uplink state "active"'),
-                          default_value=0,
-                      )),
-                     ("ready",
-                      MonitoringState(
-                          title=_('Monitoring state for uplink state "ready"'),
-                          default_value=1,
-                      )),
-                     ("not connected",
-                      MonitoringState(
-                          title=_('Monitoring state for uplink state "not connected"'),
-                          default_value=2,
-                      )),
-                     ("failed",
-                      MonitoringState(
-                          title=_('Monitoring state for uplink state "failed"'),
-                          default_value=2,
-                      )),
-                 ]
-             )),
-            # not needed, if we don't want usage -> disable in agent
-            # ('show_traffic',
-            #  FixedValue(
-            #      True,
-            #      title=_('Show bandwidth (use only with cache disabled)'),
-            #      totext='Bandwidth monitoring enabled',
-            #      help=_(
-            #          'Use only with cache disabled in the Meraki special agent settings. '
-            #          'The throughput be based on the usage for the last 60 seconds.'
-            #      )
-            #  ))
-        ],
-    )
-
-
-rulespec_registry.register(
-    CheckParameterRulespecWithItem(
-        title=lambda: _('Cisco Meraki Appliance uplinks'),
-        check_group_name='cisco_meraki_org_appliance_uplinks',
-        group=RulespecGroupCheckParametersNetworking,
-        parameter_valuespec=_parameter_valuespec_cisco_meraki_org_appliance_uplinks,
-        match_type='dict',
-        item_spec=lambda: TextInput(
-            title=_('The Uplink name'),
-        ),
-    )
-)
diff --git a/source/gui/wato/check_parameters/cisco_meraki_org_appliance_vpns.py b/source/gui/wato/check_parameters/cisco_meraki_org_appliance_vpns.py
deleted file mode 100644
index f3759c889ccc5ad8ddaf09e85c6bd6382b053189..0000000000000000000000000000000000000000
--- a/source/gui/wato/check_parameters/cisco_meraki_org_appliance_vpns.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/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-11-05
-# File  : cisco_meraki_org_appliance_vpns.py (WATO)
-
-from cmk.gui.i18n import _
-from cmk.gui.plugins.wato.utils import (
-    CheckParameterRulespecWithItem,
-    rulespec_registry,
-    RulespecGroupCheckParametersNetworking,
-)
-from cmk.gui.valuespec import (
-    Dictionary,
-    TextInput,
-    MonitoringState,
-)
-
-
-def _parameter_valuespec_cisco_meraki_org_appliance_vpns():
-    return Dictionary(
-        title=_('Cisco Meraki Appliance VPNs'),
-        optional_keys=True,
-        elements=[
-            ('status_not_reachable',
-             MonitoringState(
-                 title=_('Monitoring state if the VPN peer is not "reachable"'),
-                 default_value=1,
-             )),
-        ],
-    )
-
-
-rulespec_registry.register(
-    CheckParameterRulespecWithItem(
-        title=lambda: _('Cisco Meraki Appliance VPNs'),
-        check_group_name='cisco_meraki_org_appliance_vpns',
-        group=RulespecGroupCheckParametersNetworking,
-        parameter_valuespec=_parameter_valuespec_cisco_meraki_org_appliance_vpns,
-        match_type='dict',
-        item_spec=lambda: TextInput(
-            title=_('The peer name'),
-        ),
-    )
-)
diff --git a/source/gui/wato/check_parameters/cisco_meraki_org_device_status.py b/source/gui/wato/check_parameters/cisco_meraki_org_device_status.py
index b307596c5715ee14306475d289b00be68571b832..f24626578914de05fef76a0102ff1cb11d0a2653 100644
--- a/source/gui/wato/check_parameters/cisco_meraki_org_device_status.py
+++ b/source/gui/wato/check_parameters/cisco_meraki_org_device_status.py
@@ -8,9 +8,10 @@
 # Date  : 2023-11-04
 # File  : cisco_meraki_org_device_status.py (WATO)
 
+# 2024-06-30: moved power supply part to cisco_meraki_org_device_status_ps.py (shadow built-in file)
+
 from cmk.gui.i18n import _
 from cmk.gui.plugins.wato.utils import (
-    CheckParameterRulespecWithItem,
     CheckParameterRulespecWithoutItem,
     rulespec_registry,
     RulespecGroupCheckParametersHardware,
@@ -18,7 +19,6 @@ from cmk.gui.plugins.wato.utils import (
 from cmk.gui.valuespec import (
     Integer,
     Dictionary,
-    TextInput,
     Tuple,
     MonitoringState,
 )
@@ -79,34 +79,3 @@ rulespec_registry.register(
         match_type="dict",
     )
 )
-
-
-#
-# Cisco Meraki Power Supply
-#
-def _parameter_valuespec_cisco_meraki_device_status_ps():
-    return Dictionary(
-        title=_("Cisco Meraki Powersupply status"),
-        optional_keys=True,
-        elements=[
-            ("state_not_powering",
-             MonitoringState(
-                 title=_('Monitoring state if power supply is not "powering"'),
-                 default_value=1,
-             )),
-        ],
-    )
-
-
-rulespec_registry.register(
-    CheckParameterRulespecWithItem(
-        title=lambda: _("Cisco Meraki Power supply"),
-        check_group_name="cisco_meraki_device_status_ps",
-        group=RulespecGroupCheckParametersHardware,
-        parameter_valuespec=_parameter_valuespec_cisco_meraki_device_status_ps,
-        match_type="dict",
-        item_spec=lambda: TextInput(
-            title=_("The Slot number"),
-        ),
-    )
-)
diff --git a/source/gui/wato/check_parameters/cisco_meraki_org_device_status_ps.py b/source/gui/wato/check_parameters/cisco_meraki_org_device_status_ps.py
new file mode 100644
index 0000000000000000000000000000000000000000..335204a6c94d550cd8218e7e2807dcf73c3d09ea
--- /dev/null
+++ b/source/gui/wato/check_parameters/cisco_meraki_org_device_status_ps.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# License: GNU General Public License v2
+#
+# Author: thl-cmk[at]outlook[dot]com
+# URL   : https://thl-cmk.hopto.org
+# Date  : 2024-06-30
+# File  : cisco_meraki_org_device_status_ps.py (WATO)
+
+# 2024-06-30: created to shadow built-in file -> move rule from "Applications, Processes & Services" to "Hardware, BIOS"
+
+from cmk.gui.i18n import _
+from cmk.gui.plugins.wato.utils import (
+    CheckParameterRulespecWithItem,
+    CheckParameterRulespecWithoutItem,
+    rulespec_registry,
+    RulespecGroupCheckParametersHardware,
+)
+from cmk.gui.valuespec import (
+    Dictionary,
+    TextInput,
+    MonitoringState,
+)
+
+#
+# Cisco Meraki Power Supply -> now built-in in cmk 2.3
+#
+def _parameter_valuespec_cisco_meraki_device_status_ps():
+    return Dictionary(
+        title=_("Cisco Meraki Powersupply status"),
+        optional_keys=True,
+        elements=[
+            ("state_not_powering",
+             MonitoringState(
+                 title=_('Monitoring state if power supply is not "powering"'),
+                 default_value=1,
+             )),
+        ],
+    )
+
+
+rulespec_registry.register(
+    CheckParameterRulespecWithItem(
+        title=lambda: _("Cisco Meraki Power supply"),
+        check_group_name="cisco_meraki_device_status_ps",
+        group=RulespecGroupCheckParametersHardware,
+        parameter_valuespec=_parameter_valuespec_cisco_meraki_device_status_ps,
+        match_type="dict",
+        item_spec=lambda: TextInput(
+            title=_("Slot number"),
+        ),
+    )
+)
diff --git a/source/gui/wato/check_parameters/cisco_meraki_org_wireless_device_status.py b/source/gui/wato/check_parameters/cisco_meraki_org_wireless_device_status.py
deleted file mode 100644
index 5a408e9f9900ca2608100f908c32d7bbdfd4ad33..0000000000000000000000000000000000000000
--- a/source/gui/wato/check_parameters/cisco_meraki_org_wireless_device_status.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-#
-# License: GNU General Public License v2
-#
-# Author: thl-cmk[at]outlook[dot]com
-# URL   : https://thl-cmk.hopto.org
-# Date  : 2024-02-02
-# File  : cisco_meraki_org_wireless_status.py (WATO)
-
-from cmk.gui.i18n import _
-from cmk.gui.plugins.wato.utils import (
-    CheckParameterRulespecWithItem,
-    rulespec_registry,
-    RulespecGroupCheckParametersNetworking,
-)
-from cmk.gui.valuespec import (
-    Dictionary,
-    TextInput,
-    MonitoringState,
-)
-
-
-def _parameter_valuespec_cisco_meraki_wireless_status():
-    return Dictionary(
-        title=_('Cisco Meraki Appliance Uplinks'),
-        optional_keys=True,
-        elements=[
-            ('state_if_not_enabled',
-             MonitoringState(
-                 title=_('Monitoring state if SSID is "not enabled"'),
-                 default_value=1,
-             )),
-        ],
-    )
-
-
-rulespec_registry.register(
-    CheckParameterRulespecWithItem(
-        title=lambda: _('Cisco Meraki Wireless device'),
-        check_group_name='cisco_meraki_wireless_device_status',
-        group=RulespecGroupCheckParametersNetworking,
-        parameter_valuespec=_parameter_valuespec_cisco_meraki_wireless_status,
-        match_type='dict',
-        item_spec=lambda: TextInput(title=_('The SSID'), ),
-    )
-)
diff --git a/source/gui/wato/check_parameters/cisco_meraki_organisations.py b/source/gui/wato/check_parameters/cisco_meraki_organisations.py
deleted file mode 100644
index dcbd1a06f1fef0d93f4ae57e62222dcb6d7adc19..0000000000000000000000000000000000000000
--- a/source/gui/wato/check_parameters/cisco_meraki_organisations.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/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-11-11
-# File  : cisco_meraki_organisations.py (wato plugin)
-
-# 2023-11-18: split from cisco_meraki_org_licenses_overview.py
-
-
-from cmk.gui.i18n import _
-from cmk.gui.plugins.wato.utils import (
-    DropdownChoice,
-    HostRulespec,
-    RulespecGroupCheckParametersDiscovery,
-    rulespec_registry,
-)
-from cmk.gui.valuespec import (
-    Dictionary,
-)
-
-
-def _valuespec_discovery_meraki_organisations():
-    return Dictionary(
-        title=_('Cisco Meraki Organisations (API/Licenses)'),
-        elements=[
-            ('item_variant',
-             DropdownChoice(
-                 title=_('Information to use as item'),
-                 help=_(
-                     'You can select how to build the item for this service. By default the Organisation ID/name\n'
-                     'is used to stay compatible with the build in check. The information not used for the item\n'
-                     'will be added to the service output.'
-                 ),
-                 choices=[
-                     ('org_id', 'Organisation ID'),
-                     ('org_name', 'Organisation name'),
-                     ('org_id_name', 'Organisation ID/name'),
-                 ],
-                 default_value='org_id_name',
-             )),
-        ],
-        required_keys=['item_variant'],
-    )
-
-
-rulespec_registry.register(
-    HostRulespec(
-        group=RulespecGroupCheckParametersDiscovery,
-        match_type='dict',
-        name='discovery_meraki_organisations',
-        valuespec=_valuespec_discovery_meraki_organisations,
-    ))
diff --git a/source/gui/wato/check_parameters/cisco_meraki_organisations_api.py b/source/gui/wato/check_parameters/cisco_meraki_organisations_api.py
deleted file mode 100644
index bc47b9c0c9bf80a077853e406aec55b9e2e77193..0000000000000000000000000000000000000000
--- a/source/gui/wato/check_parameters/cisco_meraki_organisations_api.py
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/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-11-18
-# File  : cisco_meraki_organisations_api.py (wato plugin)
-
-from cmk.gui.i18n import _
-from cmk.gui.plugins.wato.utils import (
-    CheckParameterRulespecWithItem,
-    rulespec_registry,
-    RulespecGroupCheckParametersApplications,
-)
-from cmk.gui.valuespec import (
-    Dictionary,
-    TextInput,
-    MonitoringState,
-)
-
-
-def _parameter_valuespec_cisco_meraki_organisations_api():
-    return Dictionary(
-        title=_("Cisco Meraki Organisation API"),
-        optional_keys=True,
-        elements=[
-            ("state_api_not_enabled",
-             MonitoringState(
-                 title=_('Monitoring state if API is not enabled'),
-                 default_value=1,
-             )),
-        ],
-        ignored_keys=[
-            'internal_item_name',
-            'old_item_name',
-            'item_variant',
-        ],
-    )
-
-
-rulespec_registry.register(
-    CheckParameterRulespecWithItem(
-        title=lambda: _("Cisco Meraki Organisation API"),
-        check_group_name="cisco_meraki_organisations_api",
-        group=RulespecGroupCheckParametersApplications,
-        parameter_valuespec=_parameter_valuespec_cisco_meraki_organisations_api,
-        item_spec=lambda: TextInput(title=_("The organisation"), ),
-        match_type="dict",
-    )
-)
diff --git a/source/gui/wato/check_parameters/cisco_meraki_switch_ports_statuses.py b/source/gui/wato/check_parameters/cisco_meraki_switch_ports_statuses.py
deleted file mode 100644
index b312f6998a49d2141c7ad4f4c93edb480a2c8a17..0000000000000000000000000000000000000000
--- a/source/gui/wato/check_parameters/cisco_meraki_switch_ports_statuses.py
+++ /dev/null
@@ -1,132 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-#
-# License: GNU General Public License v2
-#
-# Author: thl-cmk[at]outlook[dot]com
-# URL   : https://thl-cmk.hopto.org
-# Date  : 2024-02-02
-# File  : cisco_meraki_switch_ports_statuses.py (WATO)
-
-# 2024-05-12: added support for MerakiGetOrganizationSwitchPortsStatusesBySwitch (Early Access)
-#             added traffic counters as perfdata
-# 2024-05-19: reworked switch port traffic
-# 2024-05-20: added discovery rule for port status
-
-from cmk.gui.i18n import _
-from cmk.gui.plugins.wato.utils import (
-    CheckParameterRulespecWithItem,
-    HostRulespec,
-    RulespecGroupCheckParametersDiscovery,
-    RulespecGroupCheckParametersNetworking,
-    rulespec_registry,
-)
-from cmk.gui.valuespec import (
-    Dictionary,
-    ListChoice,
-    FixedValue,
-    MonitoringState,
-    TextInput,
-)
-
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    _SEC_NAME_SWITCH_PORTS_STATUSES,   # type: ignore[import]
-    # Early Access
-    _SEC_NAME_ORG_SWITCH_PORTS_STATUSES,   # type: ignore[import]
-)
-
-
-def _parameter_valuespec_cisco_meraki_switch_ports_statuses():
-    return Dictionary(
-        title=_('Cisco Meraki Appliance Uplinks'),
-        optional_keys=True,
-        elements=[
-            ('state_disabled',
-             MonitoringState(
-                 title=_('Monitoring state if port is "disabled"'),
-                 default_value=0,
-             )),
-            ('state_not_connected',
-             MonitoringState(
-                 title=_('Monitoring state if port is "not connected"'),
-                 default_value=0,
-             )),
-            ('state_not_full_duplex',
-             MonitoringState(
-                 title=_('Monitoring state if port is "not full duplex"'),
-                 default_value=1,
-             )),
-            ('state_speed_change',
-             MonitoringState(
-                 title=_('Monitoring state if speed is changed'),
-                 default_value=1,
-             )),
-            ('state_admin_change',
-             MonitoringState(
-                 title=_('Monitoring state if admin state is changed'),
-                 default_value=1,
-             )),
-            ('state_op_change',
-             MonitoringState(
-                 title=_('Monitoring state if operational state is changed'),
-                 default_value=1,
-             )),
-            ('show_traffic',
-             FixedValue(
-                 True,
-                 title=_('Show bandwidth (use only with cache disabled)'),
-                 totext='Bandwidth monitoring enabled',
-                 help=_(
-                     'Use only with cache disabled in the Meraki special agent settings. '
-                     'Depending on your Meraki organization size (in terms of number of switches) '
-                     'this will exceeds the limits of the allowed API requests per second. You can try to '
-                     'enable "Early Access" in the Meraki dashboard. In the Meraki special agent settings '
-                     f'switch from "{_SEC_NAME_SWITCH_PORTS_STATUSES}" to "{_SEC_NAME_ORG_SWITCH_PORTS_STATUSES}". '
-                     'This will fetch all the switch data with one API request instead of one request for each switch.'
-                 ),
-             )),
-        ],
-    )
-
-
-rulespec_registry.register(
-    CheckParameterRulespecWithItem(
-        title=lambda: _('Cisco Meraki Switch Ports'),
-        check_group_name='cisco_meraki_switch_ports_statuses',
-        group=RulespecGroupCheckParametersNetworking,
-        parameter_valuespec=_parameter_valuespec_cisco_meraki_switch_ports_statuses,
-        match_type='dict',
-        item_spec=lambda: TextInput(title=_('The Port ID'), ),
-    )
-)
-
-
-
-def _valuespec_discovery_cisco_meraki_switch_ports_statuses():
-    return Dictionary(
-        title=_('Cisco Meraki Switch Ports'),
-        elements=[
-            ('discovered_port_states',
-             ListChoice(
-                 title=_('Select Ports to discover'),
-                 choices=[
-                     (True, _('Admin enabled')),
-                     (False, _('Admin disabled')),
-                     ('Connected', _('Connected')),
-                     ('Disconnected', _('Disconnected')),
-                 ],
-                 help=_('Select the port states for discovery'),
-                 default_value=[True, False, 'Connected', 'Disconnected'],
-             )),
-        ],
-        required_keys=['item_variant'],
-    )
-
-
-rulespec_registry.register(
-    HostRulespec(
-        group=RulespecGroupCheckParametersDiscovery,
-        match_type='dict',
-        name='discovery_cisco_meraki_switch_ports_statuses',
-        valuespec=_valuespec_discovery_cisco_meraki_switch_ports_statuses,
-    ))
\ No newline at end of file
diff --git a/source/packages/cisco_meraki b/source/packages/cisco_meraki
index 6f2a23d4f3b30e271bb4e1c6f6d2d6732baebe1b..901bfc25b689e41b93cb83f470c7d76b0df8f090 100644
--- a/source/packages/cisco_meraki
+++ b/source/packages/cisco_meraki
@@ -22,45 +22,48 @@
                 '\n'
                 'For the Appliance Uplinks Usage and Wireless Devices Ethernet '
                 'Statuses \n'
-                'checks you need to update the Merkai SDK to version 1.39.0 at '
+                'checks you need to update the Meraki SDK to version 1.39.0 at '
                 'least.\n'
                 'https://thl-cmk.hopto.org/gitlab/checkmk/cisco/meraki/cisco_meraki/-/raw/master/mkp/MerkaiSDK-1.39.0-202311-10.mkp\n'
                 '\n'
                 'The latest SDK can be found here: '
                 'https://github.com/meraki/dashboard-api-python\n',
  'download_url': 'https://thl-cmk.hopto.org',
- 'files': {'agent_based': ['utils/cisco_meraki.py',
-                           'cisco_meraki_org_appliance_uplinks.py',
-                           'cisco_meraki_org_appliance_vpns.py',
-                           'cisco_meraki_org_device_info.py',
+ 'files': {'agent_based': ['cisco_meraki_org_device_info.py',
                            'cisco_meraki_org_device_status.py',
-                           'cisco_meraki_org_device_uplinks.py',
                            'cisco_meraki_org_licenses_overview.py',
-                           'cisco_meraki_switch_ports_statuses.py',
-                           'cisco_meraki_org_wireless_device_status.py',
-                           'cisco_meraki_org_wireless_ethernet_statuses.py',
-                           'cisco_meraki_org_cellular_uplinks.py',
-                           'cisco_meraki_organisations_api.py',
-                           'cisco_meraki_org_networks.py',
-                           'cisco_meraki_org_appliance_performance.py'],
-           'agents': ['special/agent_cisco_meraki'],
-           'checks': ['agent_cisco_meraki'],
-           'gui': ['metrics/cisco_meraki.py',
-                   'wato/check_parameters/cisco_meraki_org_appliance_uplinks.py',
-                   'wato/check_parameters/cisco_meraki_org_appliance_vpns.py',
-                   'wato/check_parameters/cisco_meraki_org_device_status.py',
-                   'wato/check_parameters/cisco_meraki_org_licenses_overviewi.py',
-                   'wato/check_parameters/cisco_meraki_organisations.py',
-                   'wato/check_parameters/cisco_meraki_organisations_api.py',
-                   'wato/check_parameters/cisco_meraki_org_wireless_device_status.py',
-                   'wato/check_parameters/cisco_meraki_switch_ports_statuses.py',
-                   'wato/check_parameters/cisco_meraki_org_appliance_performance.py'],
-           'lib': ['python3/cmk/special_agents/agent_cisco_meraki.py'],
-           'web': ['plugins/views/cisco_meraki.py',
-                   'plugins/wato/agent_cisco_meraki.py']},
+                           'cisco_meraki_org_sensor_readings.py_'],
+           'cmk_addons_plugins': ['meraki/agent_based/appliance_performance.py',
+                                  'meraki/agent_based/appliance_uplinks.py',
+                                  'meraki/agent_based/appliance_vpns.py',
+                                  'meraki/agent_based/cellular_uplinks.py',
+                                  'meraki/agent_based/device_uplinks.py',
+                                  'meraki/agent_based/networks.py',
+                                  'meraki/agent_based/organisations_api.py',
+                                  'meraki/agent_based/switch_ports_statuses.py',
+                                  'meraki/agent_based/wireless_device_ssid_status.py',
+                                  'meraki/agent_based/wireless_ethernet_statuses.py',
+                                  'meraki/graphing/packages.py',
+                                  'meraki/lib/agent.py',
+                                  'meraki/lib/utils.py',
+                                  'meraki/rulesets/appliance_performance.py',
+                                  'meraki/rulesets/appliance_uplinks.py',
+                                  'meraki/rulesets/appliance_vpns.py',
+                                  'meraki/rulesets/licenses_overviewi.py',
+                                  'meraki/rulesets/organisations.py',
+                                  'meraki/rulesets/organisations_api.py',
+                                  'meraki/rulesets/switch_ports_statuses.py',
+                                  'meraki/rulesets/wireless_device_ssid_status.py'],
+           'cmk_plugins': ['cisco/rulesets/meraki.py',
+                           'collection/libexec/agent_cisco_meraki',
+                           'collection/server_side_calls/cisco_meraki.py'],
+           'gui': ['wato/check_parameters/cisco_meraki_org_device_status.py',
+                   'wato/check_parameters/cisco_meraki_org_device_status_ps.py',
+                   'wato/check_parameters/cisco_meraki_org_licenses_overviewi.py'],
+           'web': ['plugins/views/cisco_meraki.py']},
  'name': 'cisco_meraki',
  'title': 'Cisco Meraki special agent',
- 'version': '1.3.2-20240626',
- 'version.min_required': '2.2.0b1',
- 'version.packaged': '2.2.0p27',
- 'version.usable_until': '2.3.0b1'}
+ 'version': '1.3.2-20240660',
+ 'version.min_required': '2.3.0b1',
+ 'version.packaged': 'cmk-mkp-tool 0.2.0',
+ 'version.usable_until': '2.4.0b1'}
diff --git a/source/web/plugins/views/cisco_meraki.py b/source/web/plugins/views/cisco_meraki.py
index 89d1512e689249f5f1aac750bd88de6801f46ab4..9ec7646683492566bfc8c881df3e665e973d30cc 100644
--- a/source/web/plugins/views/cisco_meraki.py
+++ b/source/web/plugins/views/cisco_meraki.py
@@ -11,9 +11,8 @@
 # 2023-11-17: moved file from local/lib/ structure to local/share/ structure to avoid errors in web.log
 # 2023-11-19: added MT device
 
-from cmk.gui.views.inventory.registry import inventory_displayhints
-
 from cmk.gui.i18n import _l
+from cmk.gui.views.inventory.registry import inventory_displayhints
 
 inventory_displayhints.update({
     '.networking.uplinks:': {
@@ -118,9 +117,3 @@ inventory_displayhints.update({
     '.software.applications.cisco_meraki.networks:*.tags': {'title': _l('Tags')},
     '.software.applications.cisco_meraki.networks:*.is_bound_to_template': {'title': _l('Is bound to template')},
 })
-# cleanup build in display hints
-# inventory_displayhints.pop('.software.configuration.organisation')
-# inventory_displayhints.pop('.software.configuration.organisation.organisation_id')
-# inventory_displayhints.pop('.software.configuration.organisation.organisation_name')
-# inventory_displayhints.pop('.software.configuration.organisation.network_id')
-# inventory_displayhints.pop('.software.configuration.organisation.address')
diff --git a/source/web/plugins/wato/agent_cisco_meraki.py b/source/web/plugins/wato/agent_cisco_meraki.py
deleted file mode 100644
index 60192b5e871594ed67fe8e131dc12300146f1f41..0000000000000000000000000000000000000000
--- a/source/web/plugins/wato/agent_cisco_meraki.py
+++ /dev/null
@@ -1,220 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2022 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.
-
-#
-# NOTE: to use the updated cisco meraki special agent WATO via ~/local structure you need to remove/rename the
-#       original file (~/lib/check_mk/gui/plugins/wato/special_agents/cisco_meraki.py). At the moment you
-#       can not supersede a special agent wato file via ~/local structure :-(
-#
-
-# enhancements by thl-cmk[at]outlook[dot]com, https://thl-cmk.hopto.org
-# - added check for duplicate organisation IDs
-# - added some (basic) online help
-# - changed clarified title of "orgs" from "Organisations" to "Organisation IDs"
-# - added section titles from utils/cisco_meraki.py (reuse)
-# - added option for host prefix/suffix/case per organisation -> needed for dynamic host management
-# - added section names from utils/cisco_meraki.py (reuse with special agent)
-# 2023-11-18: moved from ~/local/lib/check_mk/gui/plugins/wato to ~/local/share/check_mk/web/plugins/wato
-#             changed sections to excluded_sections
-# 2023-11-22: replaced host_suffix_prefix option by org_id_as_prefix
-#             changed excluded_sections option from DualListChoice to ListChoice to avoid the "Selected" header
-#             in conjunction with "excluded Sections" title
-# 2024-05-15: added api_key to required_keys
-# 2024-06-23: added cache time per section -> not nice but should work.
-
-from typing import List
-
-from cmk.gui.i18n import _
-from cmk.gui.plugins.wato.special_agents.common import (
-    RulespecGroupDatasourceProgramsApps
-)
-from cmk.gui.plugins.wato.utils import (
-    HostRulespec,
-    HTTPProxyReference,
-    IndividualOrStoredPassword,
-    MKUserError,
-    rulespec_registry,
-)
-from cmk.gui.valuespec import (
-    Dictionary,
-    FixedValue,
-    Integer,
-    ListChoice,
-    ListOfStrings,
-    Tuple,
-    ValueSpec,
-)
-from cmk.base.plugins.agent_based.utils.cisco_meraki import (
-    # _SEC_NAME_DEVICE_INFO,
-    # _SEC_NAME_NETWORKS,
-    # _SEC_NAME_ORGANISATIONS,
-    _SEC_NAME_ORG_API_REQUESTS,  # type: ignore[import]
-    _SEC_NAME_LICENSES_OVERVIEW,  # type: ignore[import]
-    _SEC_NAME_DEVICE_STATUSES,  # type: ignore[import]
-    _SEC_NAME_SENSOR_READINGS,  # type: ignore[import]
-    _SEC_NAME_DEVICE_UPLINKS_INFO,  # type: ignore[import]
-    _SEC_NAME_APPLIANCE_UPLINKS,  # type: ignore[import]
-    _SEC_NAME_APPLIANCE_UPLINKS_USAGE,  # type: ignore[import]
-    _SEC_NAME_APPLIANCE_VPNS,  # type: ignore[import]
-    _SEC_NAME_APPLIANCE_PERFORMANCE,
-    _SEC_NAME_SWITCH_PORTS_STATUSES,  # type: ignore[import]
-    _SEC_NAME_WIRELESS_ETHERNET_STATUSES,  # type: ignore[import]
-    _SEC_NAME_WIRELESS_DEVICE_STATUS,  # type: ignore[import]
-    _SEC_NAME_CELLULAR_UPLINKS,  # type: ignore[import]
-    # Early Access
-    _SEC_NAME_ORG_SWITCH_PORTS_STATUSES,  # type: ignore[import]
-)
-
-_SEC_TITLE_DEVICE_INFO = _('Device info (Organization)')
-_SEC_TITLE_NETWORKS = _('Network info (Organization)')
-_SEC_TITLE_ORGANISATIONS = _('Organization (Agent)')
-_SEC_TITLE_ORG_API_REQUESTS = _('API request (Organizaion)')
-_SEC_TITLE_APPLIANCE_UPLINKS = _('Appliances uplinks (Organizaion)')
-_SEC_TITLE_APPLIANCE_UPLINKS_USAGE = _(
-    'Appliances uplinks usage (Organizaion)')
-_SEC_TITLE_APPLIANCE_VPNS = _('Appliances VPNs (Organizaion)')
-_SEC_TITLE_APPLIANCE_PERFORMANCE = _('Appliances Utilization (Device)')
-_SEC_TITLE_CELLULAR_UPLINKS = _('Cellular devices uplinks (Organizaion)')
-_SEC_TITLE_DEVICE_STATUSES = _('Devices status (Organizaion)')
-_SEC_TITLE_DEVICE_UPLINKS_INFO = _('Devices uplink info (Organizaion)')
-_SEC_TITLE_LICENSES_OVERVIEW = _('Licenses overview (Organizaion)')
-_SEC_TITLE_SENSOR_READINGS = _('Sensors readings (Organizaion)')
-_SEC_TITLE_SWITCH_PORTS_STATUSES = _('Switch ports status (Device)')
-_SEC_TITLE_WIRELESS_ETHERNET_STATUSES = _(
-    'Wireless devices ethernet status (Organizaion)')
-_SEC_TITLE_WIRELESS_DEVICE_STATUS = _('Wireless devices SSIDs status (Device)')
-_SEC_TITLE_ORG_SWITCH_PORTS_STATUSES = _(
-    'Switch port status (Organizaion/Early Access)')
-
-
-def _validate_orgs(value: List[str] | None, var_prefix: str):
-    # Check for duplicate Organisations
-    if value is None:
-        return
-    _p = list(set(value.copy()))
-    if len(_p) != len(value):
-        raise MKUserError(var_prefix, _('Duplicate Organisation found'))
-
-    for org_id in value:
-        if not org_id.isdigit():
-            raise MKUserError(
-                var_prefix, _(
-                    f'Not a valid Organisation ID {org_id}. Organisation IDs'
-                    ' are all digits'
-                )
-            )
-
-
-def _valuespec_special_agent_cisco_meraki() -> ValueSpec:
-    return Dictionary(
-        title=_('Cisco Meraki'),
-        elements=[
-            ('api_key', IndividualOrStoredPassword(
-                title=_('API Key'),
-                allow_empty=False,
-                help=_('The key to access the Cisco Meraki Cloud Rest API.')
-            )),
-            ('proxy', HTTPProxyReference(),),
-            ('no_cache', FixedValue(
-                value=True,
-                title=_('Disable Cache'),
-                totext=_(''),
-                help=_(
-                    'Never use cached information. By default the agent will cache received '
-                    'data to avoid API limits and speed up the data retrievel.'
-                )
-            )),
-            ('org_id_as_prefix', FixedValue(
-                value=True,
-                title=_('Uese organisation ID as host prefix'),
-                totext=_(''),
-                help=_(
-                    'The organisation ID will be used as prefix for the hostname (separated by a "\'"). Use '
-                    'this option together with a "Hostname translation for piggybacked hosts" to add a organisation '
-                    'prefix to the hosts from the Cisco Meraki cloud to avoid conflicting hostnames. You can also use '
-                    'this option along with the "Dynamic host management" to sort the host in organisation specific '
-                    'folders.'
-                )
-            )),
-            ('excluded_sections',
-             ListChoice(
-                 title=_('excluded Sections'),
-                 choices=[
-                     (_SEC_NAME_ORG_API_REQUESTS, _SEC_TITLE_ORG_API_REQUESTS),
-                     (_SEC_NAME_APPLIANCE_UPLINKS, _SEC_TITLE_APPLIANCE_UPLINKS),
-                     (_SEC_NAME_APPLIANCE_UPLINKS_USAGE,
-                      _SEC_TITLE_APPLIANCE_UPLINKS_USAGE),
-                     (_SEC_NAME_APPLIANCE_VPNS, _SEC_TITLE_APPLIANCE_VPNS),
-                     (_SEC_NAME_APPLIANCE_PERFORMANCE,
-                      _SEC_TITLE_APPLIANCE_PERFORMANCE),
-                     (_SEC_NAME_CELLULAR_UPLINKS, _SEC_TITLE_CELLULAR_UPLINKS),
-                     (_SEC_NAME_DEVICE_STATUSES, _SEC_TITLE_DEVICE_STATUSES),
-                     (_SEC_NAME_DEVICE_UPLINKS_INFO,
-                      _SEC_TITLE_DEVICE_UPLINKS_INFO),
-                     (_SEC_NAME_LICENSES_OVERVIEW, _SEC_TITLE_LICENSES_OVERVIEW),
-                     (_SEC_NAME_SENSOR_READINGS, _SEC_TITLE_SENSOR_READINGS),
-                     (_SEC_NAME_SWITCH_PORTS_STATUSES,
-                      _SEC_TITLE_SWITCH_PORTS_STATUSES),
-                     (_SEC_NAME_WIRELESS_ETHERNET_STATUSES,
-                      _SEC_TITLE_WIRELESS_ETHERNET_STATUSES),
-                     (_SEC_NAME_WIRELESS_DEVICE_STATUS,
-                      _SEC_TITLE_WIRELESS_DEVICE_STATUS),
-                     (_SEC_NAME_ORG_SWITCH_PORTS_STATUSES,
-                      _SEC_TITLE_ORG_SWITCH_PORTS_STATUSES),
-                 ],
-                 help=_(
-                     'Query only the selected sections. Default is Query all sections.'),
-                 default_value=[
-                     _SEC_NAME_ORG_SWITCH_PORTS_STATUSES,
-                     _SEC_NAME_APPLIANCE_PERFORMANCE,
-                     _SEC_NAME_SWITCH_PORTS_STATUSES,
-                     _SEC_NAME_WIRELESS_DEVICE_STATUS,
-                 ],
-             )),
-            ('orgs',
-             ListOfStrings(
-                 title=_('Organisation IDs'),
-                 help=_(
-                     'List of Organisation IDs to query. Defaulr is all Organisation IDs'),
-                 allow_empty=False,
-                 validate=_validate_orgs,
-             )),
-            ('cache_per_section',
-             Tuple(
-                 title='Set Cache time per section',
-                 elements=[
-                     Integer(title=_SEC_TITLE_APPLIANCE_PERFORMANCE, minvalue=0, unit='minutes', default_value=0),
-                     Integer(title=_SEC_TITLE_APPLIANCE_UPLINKS_USAGE, minvalue=0, unit='minutes', default_value=0),
-                     Integer(title=_SEC_TITLE_APPLIANCE_UPLINKS, minvalue=0, unit='minutes', default_value=60),
-                     Integer(title=_SEC_TITLE_APPLIANCE_VPNS, minvalue=0, unit='minutes', default_value=60),
-                     Integer(title=_SEC_TITLE_CELLULAR_UPLINKS, minvalue=0, unit='minutes', default_value=60),
-                     Integer(title=_SEC_TITLE_DEVICE_INFO, minvalue=0, unit='minutes', default_value=60),
-                     Integer(title=_SEC_TITLE_DEVICE_STATUSES, minvalue=0, unit='minutes', default_value=60),
-                     Integer(title=_SEC_TITLE_DEVICE_UPLINKS_INFO, minvalue=0, unit='minutes', default_value=60),
-                     Integer(title=_SEC_TITLE_LICENSES_OVERVIEW, minvalue=0, unit='minutes', default_value=600),
-                     Integer(title=_SEC_TITLE_NETWORKS, minvalue=0, unit='minutes', default_value=600),
-                     Integer(title=_SEC_TITLE_ORG_API_REQUESTS, minvalue=0, unit='minutes', default_value=0),
-                     Integer(title=_SEC_TITLE_ORG_SWITCH_PORTS_STATUSES, minvalue=0, unit='minutes', default_value=0),
-                     Integer(title=_SEC_TITLE_ORGANISATIONS, minvalue=0, unit='minutes', default_value=600),
-                     Integer(title=_SEC_TITLE_SENSOR_READINGS, minvalue=0, unit='minutes', default_value=0),
-                     Integer(title=_SEC_TITLE_SWITCH_PORTS_STATUSES, minvalue=0, unit='minutes', default_value=0),
-                     Integer(title=_SEC_TITLE_WIRELESS_DEVICE_STATUS, minvalue=0, unit='minutes', default_value=30),
-                     Integer(title=_SEC_TITLE_WIRELESS_ETHERNET_STATUSES, minvalue=0, unit='minutes', default_value=30),
-                 ],
-             ))
-        ],
-        optional_keys=True,
-        ignored_keys=['sections', 'host_suffix_prefix'],
-        required_keys=['excluded_sections', 'api_key'],
-    )
-
-
-rulespec_registry.register(
-    HostRulespec(
-        group=RulespecGroupDatasourceProgramsApps,
-        name='special_agents:cisco_meraki',
-        valuespec=_valuespec_special_agent_cisco_meraki,
-    )
-)