From 99ef4d6aeed5a96393d4fcd6bc66f20de0b17fce Mon Sep 17 00:00:00 2001
From: "th.l" <thl-cmk@outlook.com>
Date: Sun, 30 Jun 2024 18:42:23 +0200
Subject: [PATCH] update project

---
 README.md                                     |   2 +-
 mkp/cisco_meraki-1.3.2-20240660.mkp           | Bin 0 -> 41892 bytes
 .../cisco_meraki_org_device_info.py           |   4 +-
 .../cisco_meraki_org_device_status.py         |   6 +-
 .../cisco_meraki_org_licenses_overview.py     |  55 ++-
 .../cisco_meraki_org_sensor_readings.py_      | 240 +++++++++++++
 source/checks/agent_cisco_meraki              |  59 ----
 .../agent_based/appliance_performance.py}     |  44 +--
 .../meraki/agent_based/appliance_uplinks.py}  |  51 +--
 .../meraki/agent_based/appliance_vpns.py}     |  31 +-
 .../meraki/agent_based/cellular_uplinks.py}   | 184 +++++-----
 .../meraki/agent_based/device_uplinks.py}     |  23 +-
 .../meraki/agent_based/networks.py}           |  27 +-
 .../meraki/agent_based/organisations_api.py}  |  67 ++--
 .../agent_based/switch_ports_statuses.py}     | 112 +++---
 .../wireless_device_ssid_status.py}           |  32 +-
 .../wireless_ethernet_statuses.py}            |  77 ++--
 .../meraki/graphing/packages.py               | 238 +++++++++++++
 .../meraki/lib/agent.py}                      |  99 ++++--
 .../meraki/lib/utils.py}                      |  56 ++-
 .../meraki/rulesets/appliance_performance.py  |  63 ++++
 .../meraki/rulesets/appliance_uplinks.py      |  74 ++++
 .../meraki/rulesets/appliance_vpns.py         |  42 +++
 .../meraki/rulesets/licenses_overviewi.py     | 100 ++++++
 .../meraki/rulesets/organisations.py          |  62 ++++
 .../meraki/rulesets/organisations_api.py      |  62 ++++
 .../meraki/rulesets/switch_ports_statuses.py  | 158 +++++++++
 .../rulesets/wireless_device_ssid_status.py   |  43 +++
 source/cmk_plugins/cisco/rulesets/meraki.py   | 272 ++++++++++++++
 .../collection/libexec}/agent_cisco_meraki    |   4 +-
 .../server_side_calls/cisco_meraki.py         | 167 +++++++++
 source/gui/metrics/cisco_meraki.py            | 332 ------------------
 .../cisco_meraki_org_appliance_performance.py |  77 ----
 .../cisco_meraki_org_appliance_uplinks.py     |  82 -----
 .../cisco_meraki_org_appliance_vpns.py        |  49 ---
 .../cisco_meraki_org_device_status.py         |  35 +-
 .../cisco_meraki_org_device_status_ps.py      |  54 +++
 ...cisco_meraki_org_wireless_device_status.py |  47 ---
 .../cisco_meraki_organisations.py             |  56 ---
 .../cisco_meraki_organisations_api.py         |  52 ---
 .../cisco_meraki_switch_ports_statuses.py     | 132 -------
 source/packages/cisco_meraki                  |  67 ++--
 source/web/plugins/views/cisco_meraki.py      |   9 +-
 source/web/plugins/wato/agent_cisco_meraki.py | 220 ------------
 44 files changed, 2102 insertions(+), 1564 deletions(-)
 create mode 100644 mkp/cisco_meraki-1.3.2-20240660.mkp
 create mode 100644 source/agent_based/cisco_meraki_org_sensor_readings.py_
 delete mode 100644 source/checks/agent_cisco_meraki
 rename source/{agent_based/cisco_meraki_org_appliance_performance.py => cmk_addons_plugins/meraki/agent_based/appliance_performance.py} (65%)
 rename source/{agent_based/cisco_meraki_org_appliance_uplinks.py => cmk_addons_plugins/meraki/agent_based/appliance_uplinks.py} (86%)
 rename source/{agent_based/cisco_meraki_org_appliance_vpns.py => cmk_addons_plugins/meraki/agent_based/appliance_vpns.py} (91%)
 rename source/{agent_based/cisco_meraki_org_cellular_uplinks.py => cmk_addons_plugins/meraki/agent_based/cellular_uplinks.py} (59%)
 rename source/{agent_based/cisco_meraki_org_device_uplinks.py => cmk_addons_plugins/meraki/agent_based/device_uplinks.py} (77%)
 rename source/{agent_based/cisco_meraki_org_networks.py => cmk_addons_plugins/meraki/agent_based/networks.py} (87%)
 rename source/{agent_based/cisco_meraki_organisations_api.py => cmk_addons_plugins/meraki/agent_based/organisations_api.py} (84%)
 rename source/{agent_based/cisco_meraki_switch_ports_statuses.py => cmk_addons_plugins/meraki/agent_based/switch_ports_statuses.py} (83%)
 rename source/{agent_based/cisco_meraki_org_wireless_device_status.py => cmk_addons_plugins/meraki/agent_based/wireless_device_ssid_status.py} (88%)
 rename source/{agent_based/cisco_meraki_org_wireless_ethernet_statuses.py => cmk_addons_plugins/meraki/agent_based/wireless_ethernet_statuses.py} (75%)
 create mode 100644 source/cmk_addons_plugins/meraki/graphing/packages.py
 rename source/{lib/python3/cmk/special_agents/agent_cisco_meraki.py => cmk_addons_plugins/meraki/lib/agent.py} (95%)
 rename source/{agent_based/utils/cisco_meraki.py => cmk_addons_plugins/meraki/lib/utils.py} (65%)
 create mode 100644 source/cmk_addons_plugins/meraki/rulesets/appliance_performance.py
 create mode 100644 source/cmk_addons_plugins/meraki/rulesets/appliance_uplinks.py
 create mode 100644 source/cmk_addons_plugins/meraki/rulesets/appliance_vpns.py
 create mode 100644 source/cmk_addons_plugins/meraki/rulesets/licenses_overviewi.py
 create mode 100644 source/cmk_addons_plugins/meraki/rulesets/organisations.py
 create mode 100644 source/cmk_addons_plugins/meraki/rulesets/organisations_api.py
 create mode 100644 source/cmk_addons_plugins/meraki/rulesets/switch_ports_statuses.py
 create mode 100644 source/cmk_addons_plugins/meraki/rulesets/wireless_device_ssid_status.py
 create mode 100644 source/cmk_plugins/cisco/rulesets/meraki.py
 rename source/{agents/special => cmk_plugins/collection/libexec}/agent_cisco_meraki (80%)
 create mode 100644 source/cmk_plugins/collection/server_side_calls/cisco_meraki.py
 delete mode 100644 source/gui/metrics/cisco_meraki.py
 delete mode 100644 source/gui/wato/check_parameters/cisco_meraki_org_appliance_performance.py
 delete mode 100644 source/gui/wato/check_parameters/cisco_meraki_org_appliance_uplinks.py
 delete mode 100644 source/gui/wato/check_parameters/cisco_meraki_org_appliance_vpns.py
 create mode 100644 source/gui/wato/check_parameters/cisco_meraki_org_device_status_ps.py
 delete mode 100644 source/gui/wato/check_parameters/cisco_meraki_org_wireless_device_status.py
 delete mode 100644 source/gui/wato/check_parameters/cisco_meraki_organisations.py
 delete mode 100644 source/gui/wato/check_parameters/cisco_meraki_organisations_api.py
 delete mode 100644 source/gui/wato/check_parameters/cisco_meraki_switch_ports_statuses.py
 delete mode 100644 source/web/plugins/wato/agent_cisco_meraki.py

diff --git a/README.md b/README.md
index 50601b0..e5fe350 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
GIT binary patch
literal 41892
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
G?!N#fkU_iv

literal 0
HcmV?d00001

diff --git a/source/agent_based/cisco_meraki_org_device_info.py b/source/agent_based/cisco_meraki_org_device_info.py
index d02a911..48aacc9 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 48ebb61..56b1c6f 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 108c7ca..095d28e 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 0000000..10e9d86
--- /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 74230b8..0000000
--- 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 1ce4e1d..fce7cf5 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 5bfe043..6879d6c 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 25b76d1..e0856fe 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 0261165..06c149e 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 47b181c..26ccfc8 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 945396d..cd98c3c 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 130754d..dd06601 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 b9673ef..8409609 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 7542628..16b0345 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 046fd9a..9fc2fca 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 0000000..108400b
--- /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 e1bd3fc..860cc46 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 2ada91f..810af86 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 0000000..5424f4f
--- /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 0000000..6129d37
--- /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 0000000..3cc8cca
--- /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 0000000..cd207a6
--- /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 0000000..1161b45
--- /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 0000000..e76cc02
--- /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 0000000..3cea6f7
--- /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 0000000..8f0d170
--- /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 0000000..95fc8b3
--- /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 d4ddb88..4f569d1 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 0000000..4f772f3
--- /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 75e896c..0000000
--- 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 715f7f2..0000000
--- 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 f0ef96c..0000000
--- 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 f3759c8..0000000
--- 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 b307596..f246265 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 0000000..335204a
--- /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 5a408e9..0000000
--- 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 dcbd1a0..0000000
--- 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 bc47b9c..0000000
--- 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 b312f69..0000000
--- 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 6f2a23d..901bfc2 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 89d1512..9ec7646 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 60192b5..0000000
--- 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,
-    )
-)
-- 
GitLab