"""Appliance API endpoints.

This file is automatically @generated from the Meraki API specification.
Do not edit this file manually.

"""

from __future__ import annotations

import urllib.parse
from typing import TYPE_CHECKING, Any, Literal

from meraki_client.schemas import (
    BulkOrganizationApplianceDnsLocalProfilesAssignmentsCreateItemsItem,
    BulkOrganizationApplianceDnsLocalProfilesAssignmentsCreateResponse,
    CreateDeviceApplianceVmxAuthenticationTokenResponse,
    CreateNetworkAppliancePrefixesDelegatedStaticOrigin,
    CreateNetworkApplianceRfProfileFiveGhzSettings,
    CreateNetworkApplianceRfProfilePerSsidSettings,
    CreateNetworkApplianceRfProfileResponse,
    CreateNetworkApplianceRfProfileTwoFourGhzSettings,
    CreateNetworkApplianceStaticRouteResponse,
    CreateNetworkApplianceTrafficShapingCustomPerformanceClassResponse,
    CreateNetworkApplianceVlanDhcpOptionsItem,
    CreateNetworkApplianceVlanIpv6,
    CreateNetworkApplianceVlanMandatoryDhcp,
    CreateNetworkApplianceVlanResponse,
    CreateOrganizationApplianceDnsLocalProfileResponse,
    CreateOrganizationApplianceDnsLocalProfilesAssignmentsBulkDeleteItemsItem,
    CreateOrganizationApplianceDnsLocalProfilesAssignmentsBulkDeleteResponse,
    CreateOrganizationApplianceDnsLocalRecordProfile,
    CreateOrganizationApplianceDnsLocalRecordResponse,
    CreateOrganizationApplianceDnsSplitProfileNameservers,
    CreateOrganizationApplianceDnsSplitProfileResponse,
    CreateOrganizationApplianceDnsSplitProfilesAssignmentsBulkCreateItemsItem,
    CreateOrganizationApplianceDnsSplitProfilesAssignmentsBulkCreateResponse,
    CreateOrganizationApplianceDnsSplitProfilesAssignmentsBulkDeleteItemsItem,
    CreateOrganizationApplianceDnsSplitProfilesAssignmentsBulkDeleteResponse,
    DictResponse,
    GetDeviceApplianceDhcpSubnetsResponseItem,
    GetDeviceAppliancePerformanceResponse,
    GetDeviceAppliancePrefixesDelegatedResponse,
    GetDeviceAppliancePrefixesDelegatedVlanAssignmentsResponse,
    GetDeviceApplianceRadioSettingsResponse,
    GetDeviceApplianceUplinksSettingsResponse,
    GetNetworkApplianceClientSecurityEventsResponse,
    GetNetworkApplianceConnectivityMonitoringDestinationsResponse,
    GetNetworkApplianceContentFilteringResponse,
    GetNetworkApplianceFirewallFirewalledServiceResponse,
    GetNetworkApplianceFirewallFirewalledServicesResponseItem,
    GetNetworkApplianceFirewallInboundCellularFirewallRulesResponse,
    GetNetworkApplianceFirewallInboundFirewallRulesResponse,
    GetNetworkApplianceFirewallL7FirewallRulesApplicationCategoriesResponse,
    GetNetworkApplianceFirewallPortForwardingRulesResponse,
    GetNetworkAppliancePortResponse,
    GetNetworkAppliancePortsResponseItem,
    GetNetworkAppliancePrefixesDelegatedStaticResponse,
    GetNetworkAppliancePrefixesDelegatedStaticsResponseItem,
    GetNetworkApplianceRfProfileResponse,
    GetNetworkApplianceRfProfilesResponse,
    GetNetworkApplianceSecurityEventsResponse,
    GetNetworkApplianceSecurityIntrusionResponse,
    GetNetworkApplianceSecurityMalwareResponse,
    GetNetworkApplianceSettingsResponse,
    GetNetworkApplianceSingleLanResponse,
    GetNetworkApplianceSsidResponse,
    GetNetworkApplianceSsidsResponseItem,
    GetNetworkApplianceStaticRouteResponse,
    GetNetworkApplianceStaticRoutesResponseItem,
    GetNetworkApplianceTrafficShapingCustomPerformanceClassesResponseItem,
    GetNetworkApplianceTrafficShapingCustomPerformanceClassResponse,
    GetNetworkApplianceTrafficShapingUplinkBandwidthResponse,
    GetNetworkApplianceTrafficShapingUplinkSelectionResponse,
    GetNetworkApplianceUplinksUsageHistoryResponseItem,
    GetNetworkApplianceVlanResponse,
    GetNetworkApplianceVlansResponseItem,
    GetNetworkApplianceVlansSettingsResponse,
    GetNetworkApplianceVpnBgpResponse,
    GetNetworkApplianceVpnSiteToSiteVpnResponse,
    GetNetworkApplianceWarmSpareResponse,
    GetOrganizationApplianceDnsLocalProfilesAssignmentsResponseItemsItem,
    GetOrganizationApplianceDnsLocalProfilesResponseItem,
    GetOrganizationApplianceDnsLocalRecordsResponseItem,
    GetOrganizationApplianceDnsSplitProfilesAssignmentsResponseItemsItem,
    GetOrganizationApplianceDnsSplitProfilesResponseItem,
    GetOrganizationApplianceFirewallMulticastForwardingByNetworkResponseItemsItem,
    GetOrganizationApplianceSecurityEventsResponse,
    GetOrganizationApplianceTrafficShapingVpnExclusionsByNetworkResponseItemsItem,
    GetOrganizationApplianceUplinksStatusesOverviewResponse,
    GetOrganizationApplianceUplinkStatusesResponseItem,
    GetOrganizationApplianceUplinksUsageByNetworkResponseItem,
    GetOrganizationApplianceVpnSiteToSiteIpsecPeersSlasResponseItemsItem,
    GetOrganizationApplianceVpnStatsResponseItem,
    GetOrganizationApplianceVpnStatusesResponseItem,
    GetOrganizationApplianceVpnThirdPartyVPNPeersResponse,
    GetOrganizationApplianceVpnVpnFirewallRulesResponse,
    SwapNetworkApplianceWarmSpareResponse,
    UpdateDeviceApplianceRadioSettingsFiveGhzSettings,
    UpdateDeviceApplianceRadioSettingsResponse,
    UpdateDeviceApplianceRadioSettingsTwoFourGhzSettings,
    UpdateDeviceApplianceUplinksSettingsInterfaces,
    UpdateDeviceApplianceUplinksSettingsResponse,
    UpdateNetworkApplianceConnectivityMonitoringDestinationsDestinationsItem,
    UpdateNetworkApplianceConnectivityMonitoringDestinationsResponse,
    UpdateNetworkApplianceContentFilteringResponse,
    UpdateNetworkApplianceFirewallCellularFirewallRulesRulesItem,
    UpdateNetworkApplianceFirewallFirewalledServiceResponse,
    UpdateNetworkApplianceFirewallInboundCellularFirewallRulesResponse,
    UpdateNetworkApplianceFirewallInboundCellularFirewallRulesRulesItem,
    UpdateNetworkApplianceFirewallInboundFirewallRulesResponse,
    UpdateNetworkApplianceFirewallInboundFirewallRulesRulesItem,
    UpdateNetworkApplianceFirewallL3FirewallRulesRulesItem,
    UpdateNetworkApplianceFirewallL7FirewallRulesRulesItem,
    UpdateNetworkApplianceFirewallMulticastForwardingResponse,
    UpdateNetworkApplianceFirewallMulticastForwardingRulesItem,
    UpdateNetworkApplianceFirewallOneToManyNatRulesRulesItem,
    UpdateNetworkApplianceFirewallOneToOneNatRulesRulesItem,
    UpdateNetworkApplianceFirewallPortForwardingRulesResponse,
    UpdateNetworkApplianceFirewallPortForwardingRulesRulesItem,
    UpdateNetworkApplianceFirewallSettingsSpoofingProtection,
    UpdateNetworkAppliancePortResponse,
    UpdateNetworkAppliancePrefixesDelegatedStaticOrigin,
    UpdateNetworkApplianceRfProfileFiveGhzSettings,
    UpdateNetworkApplianceRfProfilePerSsidSettings,
    UpdateNetworkApplianceRfProfileResponse,
    UpdateNetworkApplianceRfProfileTwoFourGhzSettings,
    UpdateNetworkApplianceSdwanInternetPoliciesResponse,
    UpdateNetworkApplianceSdwanInternetPoliciesWanTrafficUplinkPreferencesItem,
    UpdateNetworkApplianceSecurityIntrusionProtectedNetworks,
    UpdateNetworkApplianceSecurityIntrusionResponse,
    UpdateNetworkApplianceSecurityMalwareAllowedFilesItem,
    UpdateNetworkApplianceSecurityMalwareAllowedUrlsItem,
    UpdateNetworkApplianceSecurityMalwareResponse,
    UpdateNetworkApplianceSettingsDynamicDns,
    UpdateNetworkApplianceSettingsResponse,
    UpdateNetworkApplianceSingleLanIpv6,
    UpdateNetworkApplianceSingleLanMandatoryDhcp,
    UpdateNetworkApplianceSingleLanResponse,
    UpdateNetworkApplianceSsidDhcpEnforcedDeauthentication,
    UpdateNetworkApplianceSsidDot11w,
    UpdateNetworkApplianceSsidRadiusServersItem,
    UpdateNetworkApplianceSsidResponse,
    UpdateNetworkApplianceStaticRouteFixedIpAssignmentsValue,
    UpdateNetworkApplianceStaticRouteReservedIpRangesItem,
    UpdateNetworkApplianceStaticRouteResponse,
    UpdateNetworkApplianceTrafficShapingCustomPerformanceClassResponse,
    UpdateNetworkApplianceTrafficShapingGlobalBandwidthLimits,
    UpdateNetworkApplianceTrafficShapingRulesRulesItem,
    UpdateNetworkApplianceTrafficShapingUplinkBandwidthBandwidthLimits,
    UpdateNetworkApplianceTrafficShapingUplinkSelectionFailoverAndFailback,
    UpdateNetworkApplianceTrafficShapingUplinkSelectionResponse,
    UpdateNetworkApplianceTrafficShapingUplinkSelectionVpnTrafficUplinkPreferencesItem,
    UpdateNetworkApplianceTrafficShapingUplinkSelectionWanTrafficUplinkPreferencesItem,
    UpdateNetworkApplianceTrafficShapingVpnExclusionsCustomItem,
    UpdateNetworkApplianceTrafficShapingVpnExclusionsMajorApplicationsItem,
    UpdateNetworkApplianceTrafficShapingVpnExclusionsResponse,
    UpdateNetworkApplianceVlanDhcpOptionsItem,
    UpdateNetworkApplianceVlanIpv6,
    UpdateNetworkApplianceVlanMandatoryDhcp,
    UpdateNetworkApplianceVlanReservedIpRangesItem,
    UpdateNetworkApplianceVlanResponse,
    UpdateNetworkApplianceVlansSettingsResponse,
    UpdateNetworkApplianceVpnBgpNeighborsItem,
    UpdateNetworkApplianceVpnBgpResponse,
    UpdateNetworkApplianceVpnSiteToSiteVpnHubsItem,
    UpdateNetworkApplianceVpnSiteToSiteVpnResponse,
    UpdateNetworkApplianceVpnSiteToSiteVpnSubnet,
    UpdateNetworkApplianceVpnSiteToSiteVpnSubnetsItem,
    UpdateNetworkApplianceWarmSpareResponse,
    UpdateOrganizationApplianceDnsLocalProfileResponse,
    UpdateOrganizationApplianceDnsLocalRecordProfile,
    UpdateOrganizationApplianceDnsLocalRecordResponse,
    UpdateOrganizationApplianceDnsSplitProfileNameservers,
    UpdateOrganizationApplianceDnsSplitProfileResponse,
    UpdateOrganizationApplianceSecurityIntrusionAllowedRulesItem,
    UpdateOrganizationApplianceVpnSiteToSiteIpsecPeersSlasItemsItem,
    UpdateOrganizationApplianceVpnSiteToSiteIpsecPeersSlasResponse,
    UpdateOrganizationApplianceVpnThirdPartyVPNPeersPeersItem,
    UpdateOrganizationApplianceVpnThirdPartyVPNPeersResponse,
    UpdateOrganizationApplianceVpnVpnFirewallRulesResponse,
    UpdateOrganizationApplianceVpnVpnFirewallRulesRulesItem,
)
from meraki_client.types import (
    CreateNetworkApplianceVlanDhcpHandling,
    CreateNetworkApplianceVlanDhcpLeaseTime,
    CreateNetworkApplianceVlanTemplateVlanType,
    GetNetworkApplianceClientSecurityEventsSortOrder,
    GetNetworkApplianceSecurityEventsSortOrder,
    GetOrganizationApplianceSecurityEventsSortOrder,
    UpdateNetworkApplianceContentFilteringUrlCategoryListSize,
    UpdateNetworkApplianceFirewallFirewalledServiceAccess,
    UpdateNetworkApplianceSecurityIntrusionIdsRulesets,
    UpdateNetworkApplianceSecurityIntrusionMode,
    UpdateNetworkApplianceSecurityMalwareMode,
    UpdateNetworkApplianceSettingsClientTrackingMethod,
    UpdateNetworkApplianceSettingsDeploymentMode,
    UpdateNetworkApplianceSsidAuthMode,
    UpdateNetworkApplianceSsidEncryptionMode,
    UpdateNetworkApplianceSsidWpaEncryptionMode,
    UpdateNetworkApplianceVlanDhcpHandling,
    UpdateNetworkApplianceVlanDhcpLeaseTime,
    UpdateNetworkApplianceVlanTemplateVlanType,
    UpdateNetworkApplianceVpnSiteToSiteVpnMode,
)

if TYPE_CHECKING:
    from meraki_client._session import PaginatedResponse, Session


class Appliance:
    """Appliance class."""

    def __init__(self, session: Session) -> None:
        self._session = session

    def get_device_appliance_dhcp_subnets(
        self, serial: str
    ) -> PaginatedResponse[GetDeviceApplianceDhcpSubnetsResponseItem]:
        """Return the DHCP subnet information for an appliance.

        [API documentation: getDeviceApplianceDhcpSubnets](https://developer.cisco.com/meraki/api-v1/#!get-device-appliance-dhcp-subnets)

        Args:
            serial: Serial.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "subnet": "192.168.1.0/24",
                "vlanId": 100,
                "usedCount": 2,
                "freeCount": 251
              }
            ]
            ```

        """
        serial = urllib.parse.quote(str(serial), safe="")
        path = f"/devices/{serial}/appliance/dhcp/subnets"

        return self._session.get_pages(
            scope="appliance",
            operation_id="getDeviceApplianceDhcpSubnets",
            path=path,
            item_schema=GetDeviceApplianceDhcpSubnetsResponseItem,
        )

    def get_device_appliance_performance(
        self,
        serial: str,
        *,
        t0: str | None = None,
        t1: str | None = None,
        timespan: float | None = None,
    ) -> GetDeviceAppliancePerformanceResponse | None:
        """Return the performance score for a single MX.

        [API documentation: getDeviceAppliancePerformance](https://developer.cisco.com/meraki/api-v1/#!get-device-appliance-performance)

        Args:
            serial: Serial.
            t0: The beginning of the timespan for the data. The maximum lookback period is 30 days
                from today.
            t1: The end of the timespan for the data. t1 can be a maximum of 14 days after t0.
            timespan: The timespan for which the information will be fetched. If specifying
                timespan, do not specify parameters t0 and t1. The value must be in
                seconds and be greater than or equal to 30 minutes and be less than or
                equal to 14 days. The default is 30 minutes.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "perfScore": 10.0
            }
            ```

        """
        serial = urllib.parse.quote(str(serial), safe="")
        path = f"/devices/{serial}/appliance/performance"

        params: dict[str, Any] = {}
        if t0 is not None:
            params["t0"] = t0
        if t1 is not None:
            params["t1"] = t1
        if timespan is not None:
            params["timespan"] = timespan

        return self._session.get(
            scope="appliance",
            operation_id="getDeviceAppliancePerformance",
            path=path,
            params=params,
            response_schema=GetDeviceAppliancePerformanceResponse,
            optional_response=True,
        )

    def get_device_appliance_prefixes_delegated(
        self, serial: str
    ) -> PaginatedResponse[GetDeviceAppliancePrefixesDelegatedResponse]:
        """Return current delegated IPv6 prefixes on an appliance.

        [API documentation: getDeviceAppliancePrefixesDelegated](https://developer.cisco.com/meraki/api-v1/#!get-device-appliance-prefixes-delegated)

        Args:
            serial: Serial.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "origin": {
                  "interface": "wan1"
                },
                "prefix": "2001:db8:3c4d:15::/64",
                "counts": {
                  "assigned": 2,
                  "available": 253
                },
                "method": "auto",
                "description": "My ISP provider",
                "isPreferred": true,
                "expiresAt": "2018-05-12T00:00:00Z"
              }
            ]
            ```

        """
        serial = urllib.parse.quote(str(serial), safe="")
        path = f"/devices/{serial}/appliance/prefixes/delegated"

        return self._session.get_pages(
            scope="appliance",
            operation_id="getDeviceAppliancePrefixesDelegated",
            path=path,
            item_schema=GetDeviceAppliancePrefixesDelegatedResponse,
        )

    def get_device_appliance_prefixes_delegated_vlan_assignments(
        self, serial: str
    ) -> PaginatedResponse[GetDeviceAppliancePrefixesDelegatedVlanAssignmentsResponse]:
        """Return prefixes assigned to all IPv6 enabled VLANs on an appliance.

        [API documentation: getDeviceAppliancePrefixesDelegatedVlanAssignments](https://developer.cisco.com/meraki/api-v1/#!get-device-appliance-prefixes-delegated-vlan-assignments)

        Args:
            serial: Serial.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "vlan": {
                  "id": 100,
                  "name": "My VLAN"
                },
                "origin": {
                  "interface": "wan1",
                  "prefix": "2001:db8:3c4d:15::/64"
                },
                "status": "Active",
                "ipv6": {
                  "prefix": "2001:db8:3c4d:15::/64",
                  "address": "2001:db8:3c4d:15::1",
                  "linkLocal": {
                    "address": "2001:db8:3c4d:15::1"
                  },
                  "solicitedNodeMulticast": {
                    "address": "2001:db8:3c4d:15::1"
                  }
                }
              }
            ]
            ```

        """
        serial = urllib.parse.quote(str(serial), safe="")
        path = f"/devices/{serial}/appliance/prefixes/delegated/vlanAssignments"

        return self._session.get_pages(
            scope="appliance",
            operation_id="getDeviceAppliancePrefixesDelegatedVlanAssignments",
            path=path,
            item_schema=GetDeviceAppliancePrefixesDelegatedVlanAssignmentsResponse,
        )

    def get_device_appliance_radio_settings(
        self, serial: str
    ) -> GetDeviceApplianceRadioSettingsResponse:
        """Return the radio settings of an appliance.

        [API documentation: getDeviceApplianceRadioSettings](https://developer.cisco.com/meraki/api-v1/#!get-device-appliance-radio-settings)

        Args:
            serial: Serial.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "serial": "Q234-ABCD-5678",
              "rfProfileId": "1234",
              "twoFourGhzSettings": {
                "channel": 11,
                "targetPower": 21
              },
              "fiveGhzSettings": {
                "channel": 149,
                "channelWidth": 20,
                "targetPower": 15
              }
            }
            ```

        """
        serial = urllib.parse.quote(str(serial), safe="")
        path = f"/devices/{serial}/appliance/radio/settings"

        return self._session.get(
            scope="appliance",
            operation_id="getDeviceApplianceRadioSettings",
            path=path,
            response_schema=GetDeviceApplianceRadioSettingsResponse,
        )

    def update_device_appliance_radio_settings(
        self,
        serial: str,
        *,
        rf_profile_id: str | None = None,
        two_four_ghz_settings: UpdateDeviceApplianceRadioSettingsTwoFourGhzSettings | None = None,
        five_ghz_settings: UpdateDeviceApplianceRadioSettingsFiveGhzSettings | None = None,
    ) -> UpdateDeviceApplianceRadioSettingsResponse:
        """Update the radio settings of an appliance.

        [API documentation: updateDeviceApplianceRadioSettings](https://developer.cisco.com/meraki/api-v1/#!update-device-appliance-radio-settings)

        Args:
            serial: Serial.
            rf_profile_id: The ID of an RF profile to assign to the device. If the value of this
                parameter is null, the appropriate basic RF profile (indoor or outdoor)
                will be assigned to the device. Assigning an RF profile will clear ALL
                manually configured overrides on the device (channel width, channel,
                power).
            two_four_ghz_settings: Manual radio settings for 2.4 GHz.
            five_ghz_settings: Manual radio settings for 5 GHz.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "serial": "Q234-ABCD-5678",
              "rfProfileId": "1234",
              "twoFourGhzSettings": {
                "channel": 11,
                "targetPower": 21
              },
              "fiveGhzSettings": {
                "channel": 149,
                "channelWidth": 20,
                "targetPower": 15
              }
            }
            ```

        """
        serial = urllib.parse.quote(str(serial), safe="")
        path = f"/devices/{serial}/appliance/radio/settings"

        payload: dict[str, Any] = {}
        if rf_profile_id is not None:
            payload["rfProfileId"] = rf_profile_id
        if two_four_ghz_settings is not None:
            payload["twoFourGhzSettings"] = two_four_ghz_settings.model_dump(
                by_alias=True, exclude_none=True
            )
        if five_ghz_settings is not None:
            payload["fiveGhzSettings"] = five_ghz_settings.model_dump(
                by_alias=True, exclude_none=True
            )

        return self._session.put(
            scope="appliance",
            operation_id="updateDeviceApplianceRadioSettings",
            path=path,
            json=payload,
            response_schema=UpdateDeviceApplianceRadioSettingsResponse,
        )

    def get_device_appliance_uplinks_settings(
        self, serial: str
    ) -> GetDeviceApplianceUplinksSettingsResponse:
        """Return the uplink settings for an MX appliance.

        [API documentation: getDeviceApplianceUplinksSettings](https://developer.cisco.com/meraki/api-v1/#!get-device-appliance-uplinks-settings)

        Args:
            serial: Serial.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "interfaces": {
                "wan1": {
                  "enabled": true,
                  "vlanTagging": {
                    "enabled": true,
                    "vlanId": 1
                  },
                  "svis": {
                    "ipv4": {
                      "assignmentMode": "static",
                      "address": "9.10.11.10/16",
                      "gateway": "13.14.15.16",
                      "nameservers": {
                        "addresses": [
                          "1.2.3.4"
                        ]
                      }
                    },
                    "ipv6": {
                      "assignmentMode": "static",
                      "address": "1:2:3::4",
                      "gateway": "1:2:3::5",
                      "nameservers": {
                        "addresses": [
                          "1001:4860:4860::8888",
                          "1001:4860:4860::8844"
                        ]
                      }
                    }
                  },
                  "pppoe": {
                    "enabled": true,
                    "authentication": {
                      "enabled": true,
                      "username": "username"
                    }
                  }
                },
                "wan2": {
                  "enabled": true,
                  "vlanTagging": {
                    "enabled": true,
                    "vlanId": 1
                  },
                  "svis": {
                    "ipv4": {
                      "assignmentMode": "static",
                      "address": "9.10.11.10/16",
                      "gateway": "13.14.15.16",
                      "nameservers": {
                        "addresses": [
                          "1.2.3.4"
                        ]
                      }
                    },
                    "ipv6": {
                      "assignmentMode": "static",
                      "address": "1:2:3::4",
                      "gateway": "1:2:3::5",
                      "nameservers": {
                        "addresses": [
                          "1001:4860:4860::8888",
                          "1001:4860:4860::8844"
                        ]
                      }
                    }
                  },
                  "pppoe": {
                    "enabled": true,
                    "authentication": {
                      "enabled": true,
                      "username": "username"
                    }
                  }
                }
              }
            }
            ```

        """
        serial = urllib.parse.quote(str(serial), safe="")
        path = f"/devices/{serial}/appliance/uplinks/settings"

        return self._session.get(
            scope="appliance",
            operation_id="getDeviceApplianceUplinksSettings",
            path=path,
            response_schema=GetDeviceApplianceUplinksSettingsResponse,
        )

    def update_device_appliance_uplinks_settings(
        self, *, serial: str, interfaces: UpdateDeviceApplianceUplinksSettingsInterfaces
    ) -> UpdateDeviceApplianceUplinksSettingsResponse:
        """Update the uplink settings for an MX appliance.

        [API documentation: updateDeviceApplianceUplinksSettings](https://developer.cisco.com/meraki/api-v1/#!update-device-appliance-uplinks-settings)

        Args:
            serial: Serial.
            interfaces: Interface settings.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "interfaces": {
                "wan1": {
                  "enabled": true,
                  "vlanTagging": {
                    "enabled": true,
                    "vlanId": 1
                  },
                  "svis": {
                    "ipv4": {
                      "assignmentMode": "static",
                      "address": "9.10.11.10/16",
                      "gateway": "13.14.15.16",
                      "nameservers": {
                        "addresses": [
                          "1.2.3.4"
                        ]
                      }
                    },
                    "ipv6": {
                      "assignmentMode": "static",
                      "address": "1:2:3::4",
                      "gateway": "1:2:3::5",
                      "nameservers": {
                        "addresses": [
                          "1001:4860:4860::8888",
                          "1001:4860:4860::8844"
                        ]
                      }
                    }
                  },
                  "pppoe": {
                    "enabled": true,
                    "authentication": {
                      "enabled": true,
                      "username": "username"
                    }
                  }
                },
                "wan2": {
                  "enabled": true,
                  "vlanTagging": {
                    "enabled": true,
                    "vlanId": 1
                  },
                  "svis": {
                    "ipv4": {
                      "assignmentMode": "static",
                      "address": "9.10.11.10/16",
                      "gateway": "13.14.15.16",
                      "nameservers": {
                        "addresses": [
                          "1.2.3.4"
                        ]
                      }
                    },
                    "ipv6": {
                      "assignmentMode": "static",
                      "address": "1:2:3::4",
                      "gateway": "1:2:3::5",
                      "nameservers": {
                        "addresses": [
                          "1001:4860:4860::8888",
                          "1001:4860:4860::8844"
                        ]
                      }
                    }
                  },
                  "pppoe": {
                    "enabled": true,
                    "authentication": {
                      "enabled": true,
                      "username": "username"
                    }
                  }
                }
              }
            }
            ```

        """
        serial = urllib.parse.quote(str(serial), safe="")
        path = f"/devices/{serial}/appliance/uplinks/settings"

        payload: dict[str, Any] = {}
        if interfaces is not None:
            payload["interfaces"] = interfaces.model_dump(by_alias=True, exclude_none=True)

        return self._session.put(
            scope="appliance",
            operation_id="updateDeviceApplianceUplinksSettings",
            path=path,
            json=payload,
            response_schema=UpdateDeviceApplianceUplinksSettingsResponse,
        )

    def create_device_appliance_vmx_authentication_token(
        self, serial: str
    ) -> CreateDeviceApplianceVmxAuthenticationTokenResponse:
        """Generate a new vMX authentication token.

        [API documentation: createDeviceApplianceVmxAuthenticationToken](https://developer.cisco.com/meraki/api-v1/#!create-device-appliance-vmx-authentication-token)

        Args:
            serial: Serial.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "token": "ffc0b7b578b61be5bd1d172132c78044/9af03bd84d474",
              "expiresAt": "2021-06-18T12:40:10Z"
            }
            ```

        """
        serial = urllib.parse.quote(str(serial), safe="")
        path = f"/devices/{serial}/appliance/vmx/authenticationToken"

        return self._session.post(
            scope="appliance",
            operation_id="createDeviceApplianceVmxAuthenticationToken",
            path=path,
            response_schema=CreateDeviceApplianceVmxAuthenticationTokenResponse,
        )

    def get_network_appliance_client_security_events(
        self,
        *,
        network_id: str,
        client_id: str,
        t0: str | None = None,
        t1: str | None = None,
        timespan: float | None = None,
        per_page: int | None = None,
        starting_after: str | None = None,
        ending_before: str | None = None,
        sort_order: GetNetworkApplianceClientSecurityEventsSortOrder | None = None,
        total_pages: int | Literal["all"] = "all",
        direction: Literal["prev", "next"] = "next",
    ) -> PaginatedResponse[GetNetworkApplianceClientSecurityEventsResponse]:
        """List the security events for a client.

        [API documentation: getNetworkApplianceClientSecurityEvents](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-client-security-events)

        Args:
            network_id: Network ID.
            client_id: Client ID.
            t0: The beginning of the timespan for the data. Data is gathered after the specified t0
                value. The maximum lookback period is 791 days from today.
            t1: The end of the timespan for the data. t1 can be a maximum of 791 days after t0.
            timespan: The timespan for which the information will be fetched. If specifying
                timespan, do not specify parameters t0 and t1. The value must be in
                seconds and be less than or equal to 791 days. The default is 31 days.
            per_page: The number of entries per page returned. Acceptable range is 3 - 1000. Default
                is 100.
            starting_after: A token used by the server to indicate the start of the page. Often this
                is a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            ending_before: A token used by the server to indicate the end of the page. Often this is
                a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            sort_order: Sorted order of security events based on event detection time. Order options
                are 'ascending' or 'descending'. Default is ascending order.
            total_pages: use with per_page to get total results up to total_pages * per_page; -1 or
                "all" for all pages.
            direction: direction to paginate, either "next" (default) or "prev" page.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "ts": "2018-02-11T00:00:00.090210Z",
                "eventType": "File Scanned",
                "clientName": "COMPUTER-M-V78J",
                "clientMac": "10:dd:b1:eb:88:f8",
                "clientIp": "192.168.128.2",
                "srcIp": "192.168.128.2",
                "destIp": "119.192.233.48",
                "protocol": "http",
                "uri": "http://www.favorite-icons.com/program/FavoriteIconsUninstall.exe",
                "canonicalName": "PUA.Win.Dropper.Kraddare::1201",
                "destinationPort": 80,
                "fileHash": "3ec1b9a95fe62aa25fc959643a0f227b76d253094681934daaf628d3574b3463",
                "fileType": "MS_EXE",
                "fileSizeBytes": 193688,
                "disposition": "Malicious",
                "action": "Blocked"
              },
              {
                "ts": "2018-02-11T00:00:00.090210Z",
                "eventType": "IDS Alert",
                "deviceMac": "00:18:0a:01:02:03",
                "clientMac": "A1:B2:C3:D4:E5:F6",
                "srcIp": "1.2.3.4:34195",
                "destIp": "10.20.30.40:80",
                "protocol": "tcp/ip",
                "priority": "2",
                "classification": "4",
                "blocked": true,
                "message": "SERVER-WEBAPP JBoss JMX console access attempt",
                "signature": "1:21516:9",
                "sigSource": "",
                "ruleId": "meraki:intrusion/snort/GID/1/SID/26267"
              }
            ]
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        client_id = urllib.parse.quote(str(client_id), safe="")
        path = f"/networks/{network_id}/appliance/clients/{client_id}/security/events"

        params: dict[str, Any] = {}
        if t0 is not None:
            params["t0"] = t0
        if t1 is not None:
            params["t1"] = t1
        if timespan is not None:
            params["timespan"] = timespan
        if per_page is not None:
            params["perPage"] = per_page
        if starting_after is not None:
            params["startingAfter"] = starting_after
        if ending_before is not None:
            params["endingBefore"] = ending_before
        if sort_order is not None:
            params["sortOrder"] = sort_order

        return self._session.get_pages(
            scope="appliance",
            operation_id="getNetworkApplianceClientSecurityEvents",
            path=path,
            params=params,
            total_pages=total_pages,
            direction=direction,
            item_schema=GetNetworkApplianceClientSecurityEventsResponse,
        )

    def get_network_appliance_connectivity_monitoring_destinations(
        self, network_id: str
    ) -> GetNetworkApplianceConnectivityMonitoringDestinationsResponse:
        """Return the connectivity testing destinations for an MX network.

        [API documentation: getNetworkApplianceConnectivityMonitoringDestinations](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-connectivity-monitoring-destinations)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "destinations": [
                {
                  "ip": "1.2.3.4",
                  "description": "Google",
                  "default": false
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/connectivityMonitoringDestinations"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceConnectivityMonitoringDestinations",
            path=path,
            response_schema=GetNetworkApplianceConnectivityMonitoringDestinationsResponse,
        )

    def update_network_appliance_connectivity_monitoring_destinations(
        self,
        network_id: str,
        *,
        destinations: list[UpdateNetworkApplianceConnectivityMonitoringDestinationsDestinationsItem]
        | None = None,
    ) -> UpdateNetworkApplianceConnectivityMonitoringDestinationsResponse:
        """Update the connectivity testing destinations for an MX network.

        [API documentation: updateNetworkApplianceConnectivityMonitoringDestinations](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-connectivity-monitoring-destinations)

        Args:
            network_id: Network ID.
            destinations: The list of connectivity monitoring destinations.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "destinations": [
                {
                  "ip": "1.2.3.4",
                  "description": "Google",
                  "default": false
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/connectivityMonitoringDestinations"

        payload: dict[str, Any] = {}
        if destinations is not None:
            payload["destinations"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in destinations
            ]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceConnectivityMonitoringDestinations",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceConnectivityMonitoringDestinationsResponse,
        )

    def get_network_appliance_content_filtering(
        self, network_id: str
    ) -> GetNetworkApplianceContentFilteringResponse:
        """Return the content filtering settings for an MX network.

        [API documentation: getNetworkApplianceContentFiltering](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-content-filtering)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "allowedUrlPatterns": [
                "http://www.example.org",
                "http://help.com.au"
              ],
              "blockedUrlPatterns": [
                "http://www.example.com",
                "http://www.betting.com"
              ],
              "blockedUrlCategories": [
                {
                  "id": "meraki:contentFiltering/category/1",
                  "name": "Real Estate"
                }
              ],
              "urlCategoryListSize": "topSites"
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/contentFiltering"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceContentFiltering",
            path=path,
            response_schema=GetNetworkApplianceContentFilteringResponse,
        )

    def update_network_appliance_content_filtering(
        self,
        network_id: str,
        *,
        allowed_url_patterns: list[str] | None = None,
        blocked_url_patterns: list[str] | None = None,
        blocked_url_categories: list[str] | None = None,
        url_category_list_size: UpdateNetworkApplianceContentFilteringUrlCategoryListSize
        | None = None,
    ) -> UpdateNetworkApplianceContentFilteringResponse:
        """Update the content filtering settings for an MX network.

        [API documentation: updateNetworkApplianceContentFiltering](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-content-filtering)

        Args:
            network_id: Network ID.
            allowed_url_patterns: A list of URL patterns that are allowed.
            blocked_url_patterns: A list of URL patterns that are blocked.
            blocked_url_categories: A list of URL categories to block.
            url_category_list_size: URL category list size which is either 'topSites' or 'fullList'.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "allowedUrlPatterns": [
                "http://www.example.org",
                "http://help.com.au"
              ],
              "blockedUrlPatterns": [
                "http://www.example.com",
                "http://www.betting.com"
              ],
              "blockedUrlCategories": [
                {
                  "id": "meraki:contentFiltering/category/1",
                  "name": "Real Estate"
                }
              ],
              "urlCategoryListSize": "topSites"
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/contentFiltering"

        payload: dict[str, Any] = {}
        if allowed_url_patterns is not None:
            payload["allowedUrlPatterns"] = allowed_url_patterns
        if blocked_url_patterns is not None:
            payload["blockedUrlPatterns"] = blocked_url_patterns
        if blocked_url_categories is not None:
            payload["blockedUrlCategories"] = blocked_url_categories
        if url_category_list_size is not None:
            payload["urlCategoryListSize"] = url_category_list_size

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceContentFiltering",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceContentFilteringResponse,
        )

    def get_network_appliance_content_filtering_categories(self, network_id: str) -> DictResponse:
        """List all available content filtering categories for an MX network.

        [API documentation: getNetworkApplianceContentFilteringCategories](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-content-filtering-categories)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "categories": [
                {
                  "id": "meraki:contentFiltering/category/1",
                  "name": "Real Estate"
                },
                {
                  "id": "meraki:contentFiltering/category/3",
                  "name": "Financial Services"
                },
                "...",
                {
                  "id": "meraki:contentFiltering/category/11",
                  "name": "Adult and Pornography"
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/contentFiltering/categories"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceContentFilteringCategories",
            path=path,
            response_schema=DictResponse,
        )

    def get_network_appliance_firewall_cellular_firewall_rules(
        self, network_id: str
    ) -> DictResponse:
        """Return the cellular firewall rules for an MX network.

        [API documentation: getNetworkApplianceFirewallCellularFirewallRules](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-cellular-firewall-rules)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "comment": "Allow TCP traffic to subnet with HTTP servers.",
                  "policy": "allow",
                  "protocol": "tcp",
                  "destPort": "443",
                  "destCidr": "192.168.1.0/24",
                  "srcPort": "Any",
                  "srcCidr": "Any",
                  "syslogEnabled": false
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/cellularFirewallRules"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallCellularFirewallRules",
            path=path,
            response_schema=DictResponse,
        )

    def update_network_appliance_firewall_cellular_firewall_rules(
        self,
        network_id: str,
        *,
        rules: list[UpdateNetworkApplianceFirewallCellularFirewallRulesRulesItem] | None = None,
    ) -> DictResponse:
        """Update the cellular firewall rules of an MX network.

        [API documentation: updateNetworkApplianceFirewallCellularFirewallRules](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-firewall-cellular-firewall-rules)

        Args:
            network_id: Network ID.
            rules: An ordered array of the firewall rules (not including the default rule).

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "comment": "Allow TCP traffic to subnet with HTTP servers.",
                  "policy": "allow",
                  "protocol": "tcp",
                  "destPort": "443",
                  "destCidr": "192.168.1.0/24",
                  "srcPort": "Any",
                  "srcCidr": "Any",
                  "syslogEnabled": false
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/cellularFirewallRules"

        payload: dict[str, Any] = {}
        if rules is not None:
            payload["rules"] = [item.model_dump(by_alias=True, exclude_none=True) for item in rules]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceFirewallCellularFirewallRules",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def get_network_appliance_firewall_firewalled_services(
        self, network_id: str
    ) -> PaginatedResponse[GetNetworkApplianceFirewallFirewalledServicesResponseItem]:
        """List the appliance services and their accessibility rules.

        [API documentation: getNetworkApplianceFirewallFirewalledServices](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-firewalled-services)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "service": "web",
                "access": "restricted",
                "allowedIps": [
                  "123.123.123.1"
                ]
              }
            ]
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/firewalledServices"

        return self._session.get_pages(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallFirewalledServices",
            path=path,
            item_schema=GetNetworkApplianceFirewallFirewalledServicesResponseItem,
        )

    def get_network_appliance_firewall_firewalled_service(
        self, *, network_id: str, service: str
    ) -> GetNetworkApplianceFirewallFirewalledServiceResponse:
        """Return the accessibility settings of the given service ('ICMP', 'web', or 'SNMP').

        [API documentation: getNetworkApplianceFirewallFirewalledService](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-firewalled-service)

        Args:
            network_id: Network ID.
            service: Service.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "service": "web",
              "access": "restricted",
              "allowedIps": [
                "123.123.123.1"
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        service = urllib.parse.quote(str(service), safe="")
        path = f"/networks/{network_id}/appliance/firewall/firewalledServices/{service}"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallFirewalledService",
            path=path,
            response_schema=GetNetworkApplianceFirewallFirewalledServiceResponse,
        )

    def update_network_appliance_firewall_firewalled_service(
        self,
        *,
        network_id: str,
        service: str,
        access: UpdateNetworkApplianceFirewallFirewalledServiceAccess,
        allowed_ips: list[str] | None = None,
    ) -> UpdateNetworkApplianceFirewallFirewalledServiceResponse:
        """Updates the accessibility settings for the given service ('ICMP', 'web', or 'SNMP').

        [API documentation: updateNetworkApplianceFirewallFirewalledService](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-firewall-firewalled-service)

        Args:
            network_id: Network ID.
            service: Service.
            access: A string indicating the rule for which IPs are allowed to use the specified
                service. Acceptable values are "blocked" (no remote IPs can access the
                service), "restricted" (only allowed IPs can access the service), and
                "unrestriced" (any remote IP can access the service). This field is
                required.
            allowed_ips: An array of allowed CIDRs that can access the service. This field is
                required if "access" is set to "restricted". Otherwise this field is
                ignored.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "service": "web",
              "access": "restricted",
              "allowedIps": [
                "123.123.123.1"
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        service = urllib.parse.quote(str(service), safe="")
        path = f"/networks/{network_id}/appliance/firewall/firewalledServices/{service}"

        payload: dict[str, Any] = {}
        if access is not None:
            payload["access"] = access
        if allowed_ips is not None:
            payload["allowedIps"] = allowed_ips

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceFirewallFirewalledService",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceFirewallFirewalledServiceResponse,
        )

    def get_network_appliance_firewall_inbound_cellular_firewall_rules(
        self, network_id: str
    ) -> GetNetworkApplianceFirewallInboundCellularFirewallRulesResponse:
        """Return the inbound cellular firewall rules for an MX network.

        [API documentation: getNetworkApplianceFirewallInboundCellularFirewallRules](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-inbound-cellular-firewall-rules)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "comment": "Allow TCP traffic to subnet with HTTP servers.",
                  "policy": "allow",
                  "protocol": "tcp",
                  "srcPort": "Any",
                  "srcCidr": "Any",
                  "destPort": "443",
                  "destCidr": "192.168.1.0/24",
                  "syslogEnabled": false
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/inboundCellularFirewallRules"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallInboundCellularFirewallRules",
            path=path,
            response_schema=GetNetworkApplianceFirewallInboundCellularFirewallRulesResponse,
        )

    def update_network_appliance_firewall_inbound_cellular_firewall_rules(
        self,
        network_id: str,
        *,
        rules: list[UpdateNetworkApplianceFirewallInboundCellularFirewallRulesRulesItem]
        | None = None,
    ) -> UpdateNetworkApplianceFirewallInboundCellularFirewallRulesResponse:
        """Update the inbound cellular firewall rules of an MX network.

        [API documentation: updateNetworkApplianceFirewallInboundCellularFirewallRules](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-firewall-inbound-cellular-firewall-rules)

        Args:
            network_id: Network ID.
            rules: An ordered array of the firewall rules (not including the default rule).

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "comment": "Allow TCP traffic to subnet with HTTP servers.",
                  "policy": "allow",
                  "protocol": "tcp",
                  "srcPort": "Any",
                  "srcCidr": "Any",
                  "destPort": "443",
                  "destCidr": "192.168.1.0/24",
                  "syslogEnabled": false
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/inboundCellularFirewallRules"

        payload: dict[str, Any] = {}
        if rules is not None:
            payload["rules"] = [item.model_dump(by_alias=True, exclude_none=True) for item in rules]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceFirewallInboundCellularFirewallRules",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceFirewallInboundCellularFirewallRulesResponse,
        )

    def get_network_appliance_firewall_inbound_firewall_rules(
        self, network_id: str
    ) -> GetNetworkApplianceFirewallInboundFirewallRulesResponse:
        """Return the inbound firewall rules for an MX network.

        [API documentation: getNetworkApplianceFirewallInboundFirewallRules](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-inbound-firewall-rules)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "comment": "Allow TCP traffic to subnet with HTTP servers.",
                  "policy": "allow",
                  "protocol": "tcp",
                  "srcPort": "Any",
                  "srcCidr": "Any",
                  "destPort": "443",
                  "destCidr": "192.168.1.0/24",
                  "syslogEnabled": false
                }
              ],
              "syslogDefaultRule": false
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/inboundFirewallRules"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallInboundFirewallRules",
            path=path,
            response_schema=GetNetworkApplianceFirewallInboundFirewallRulesResponse,
        )

    def update_network_appliance_firewall_inbound_firewall_rules(
        self,
        network_id: str,
        *,
        rules: list[UpdateNetworkApplianceFirewallInboundFirewallRulesRulesItem] | None = None,
        syslog_default_rule: bool | None = None,
    ) -> UpdateNetworkApplianceFirewallInboundFirewallRulesResponse:
        """Update the inbound firewall rules of an MX network.

        [API documentation: updateNetworkApplianceFirewallInboundFirewallRules](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-firewall-inbound-firewall-rules)

        Args:
            network_id: Network ID.
            rules: An ordered array of the firewall rules (not including the default rule).
            syslog_default_rule: Log the special default rule (boolean value - enable only if you've
                configured a syslog server) (optional).

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "comment": "Allow TCP traffic to subnet with HTTP servers.",
                  "policy": "allow",
                  "protocol": "tcp",
                  "srcPort": "Any",
                  "srcCidr": "Any",
                  "destPort": "443",
                  "destCidr": "192.168.1.0/24",
                  "syslogEnabled": false
                }
              ],
              "syslogDefaultRule": false
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/inboundFirewallRules"

        payload: dict[str, Any] = {}
        if rules is not None:
            payload["rules"] = [item.model_dump(by_alias=True, exclude_none=True) for item in rules]
        if syslog_default_rule is not None:
            payload["syslogDefaultRule"] = syslog_default_rule

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceFirewallInboundFirewallRules",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceFirewallInboundFirewallRulesResponse,
        )

    def get_network_appliance_firewall_l3_firewall_rules(self, network_id: str) -> DictResponse:
        """Return the L3 firewall rules for an MX network.

        [API documentation: getNetworkApplianceFirewallL3FirewallRules](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-l-3-firewall-rules)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "comment": "Allow TCP traffic to subnet with HTTP servers.",
                  "policy": "allow",
                  "protocol": "tcp",
                  "destPort": "443",
                  "destCidr": "192.168.1.0/24",
                  "srcPort": "Any",
                  "srcCidr": "Any",
                  "syslogEnabled": false
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/l3FirewallRules"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallL3FirewallRules",
            path=path,
            response_schema=DictResponse,
        )

    def update_network_appliance_firewall_l3_firewall_rules(
        self,
        network_id: str,
        *,
        rules: list[UpdateNetworkApplianceFirewallL3FirewallRulesRulesItem] | None = None,
        syslog_default_rule: bool | None = None,
    ) -> DictResponse:
        """Update the L3 firewall rules of an MX network.

        [API documentation: updateNetworkApplianceFirewallL3FirewallRules](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-firewall-l-3-firewall-rules)

        Args:
            network_id: Network ID.
            rules: An ordered array of the firewall rules (not including the default rule).
            syslog_default_rule: Log the special default rule (boolean value - enable only if you've
                configured a syslog server) (optional).

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "comment": "Allow TCP traffic to subnet with HTTP servers.",
                  "policy": "allow",
                  "protocol": "tcp",
                  "destPort": "443",
                  "destCidr": "192.168.1.0/24",
                  "srcPort": "Any",
                  "srcCidr": "Any",
                  "syslogEnabled": false
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/l3FirewallRules"

        payload: dict[str, Any] = {}
        if rules is not None:
            payload["rules"] = [item.model_dump(by_alias=True, exclude_none=True) for item in rules]
        if syslog_default_rule is not None:
            payload["syslogDefaultRule"] = syslog_default_rule

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceFirewallL3FirewallRules",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def get_network_appliance_firewall_l7_firewall_rules(self, network_id: str) -> DictResponse:
        """List the MX L7 firewall rules for an MX network.

        [API documentation: getNetworkApplianceFirewallL7FirewallRules](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-l-7-firewall-rules)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "policy": "deny",
                  "type": "host",
                  "value": "google.com"
                },
                {
                  "policy": "deny",
                  "type": "port",
                  "value": "23"
                },
                {
                  "policy": "deny",
                  "type": "ipRange",
                  "value": "10.11.12.00/24"
                },
                {
                  "policy": "deny",
                  "type": "ipRange",
                  "value": "10.11.12.00/24:5555"
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/l7FirewallRules"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallL7FirewallRules",
            path=path,
            response_schema=DictResponse,
        )

    def update_network_appliance_firewall_l7_firewall_rules(
        self,
        network_id: str,
        *,
        rules: list[UpdateNetworkApplianceFirewallL7FirewallRulesRulesItem] | None = None,
    ) -> DictResponse:
        """Update the MX L7 firewall rules for an MX network.

        [API documentation: updateNetworkApplianceFirewallL7FirewallRules](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-firewall-l-7-firewall-rules)

        Args:
            network_id: Network ID.
            rules: An ordered array of the MX L7 firewall rules.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "policy": "deny",
                  "type": "host",
                  "value": "google.com"
                },
                {
                  "policy": "deny",
                  "type": "port",
                  "value": "23"
                },
                {
                  "policy": "deny",
                  "type": "ipRange",
                  "value": "10.11.12.00/24"
                },
                {
                  "policy": "deny",
                  "type": "ipRange",
                  "value": "10.11.12.00/24:5555"
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/l7FirewallRules"

        payload: dict[str, Any] = {}
        if rules is not None:
            payload["rules"] = [item.model_dump(by_alias=True, exclude_none=True) for item in rules]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceFirewallL7FirewallRules",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def get_network_appliance_firewall_l7_firewall_rules_application_categories(
        self, network_id: str
    ) -> GetNetworkApplianceFirewallL7FirewallRulesApplicationCategoriesResponse:
        """Return the L7 firewall application categories and their associated applications for an MX network.

        [API documentation: getNetworkApplianceFirewallL7FirewallRulesApplicationCategories](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-l-7-firewall-rules-application-categories)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "applicationCategories": [
                {
                  "id": "meraki:layer7/category/24",
                  "name": "Advertising",
                  "applications": [
                    {
                      "id": "meraki:layer7/application/5",
                      "name": "Advertising.com"
                    }
                  ]
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/l7FirewallRules/applicationCategories"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallL7FirewallRulesApplicationCategories",
            path=path,
            response_schema=GetNetworkApplianceFirewallL7FirewallRulesApplicationCategoriesResponse,
        )

    def update_network_appliance_firewall_multicast_forwarding(
        self,
        *,
        network_id: str,
        rules: list[UpdateNetworkApplianceFirewallMulticastForwardingRulesItem],
    ) -> UpdateNetworkApplianceFirewallMulticastForwardingResponse:
        """Update static multicast forward rules for a network.

        [API documentation: updateNetworkApplianceFirewallMulticastForwarding](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-firewall-multicast-forwarding)

        Args:
            network_id: Network ID.
            rules: Static multicast forwarding rules. Pass an empty array to clear all rules.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "network": {
                "id": "N_24329156",
                "name": "Main Office"
              },
              "rules": [
                {
                  "description": "test",
                  "address": "224.0.0.1",
                  "vlanIds": [
                    "1"
                  ]
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/multicastForwarding"

        payload: dict[str, Any] = {}
        if rules is not None:
            payload["rules"] = [item.model_dump(by_alias=True, exclude_none=True) for item in rules]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceFirewallMulticastForwarding",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceFirewallMulticastForwardingResponse,
        )

    def get_network_appliance_firewall_one_to_many_nat_rules(self, network_id: str) -> DictResponse:
        """Return the 1:Many NAT mapping rules for an MX network.

        [API documentation: getNetworkApplianceFirewallOneToManyNatRules](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-one-to-many-nat-rules)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "publicIp": "146.11.11.13",
                  "uplink": "internet1",
                  "portRules": [
                    {
                      "name": "Rule 1",
                      "protocol": "tcp",
                      "publicPort": "9443",
                      "localIp": "192.168.128.1",
                      "localPort": "443",
                      "allowedIps": [
                        "any"
                      ]
                    },
                    {
                      "name": "Rule 2",
                      "protocol": "tcp",
                      "publicPort": "8080",
                      "localIp": "192.168.128.1",
                      "localPort": "80",
                      "allowedIps": [
                        "10.82.110.0/24",
                        "10.82.111.0/24"
                      ]
                    }
                  ]
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/oneToManyNatRules"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallOneToManyNatRules",
            path=path,
            response_schema=DictResponse,
        )

    def update_network_appliance_firewall_one_to_many_nat_rules(
        self,
        *,
        network_id: str,
        rules: list[UpdateNetworkApplianceFirewallOneToManyNatRulesRulesItem],
    ) -> DictResponse:
        """Set the 1:Many NAT mapping rules for an MX network.

        [API documentation: updateNetworkApplianceFirewallOneToManyNatRules](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-firewall-one-to-many-nat-rules)

        Args:
            network_id: Network ID.
            rules: An array of 1:Many nat rules.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "publicIp": "146.11.11.13",
                  "uplink": "internet1",
                  "portRules": [
                    {
                      "name": "Rule 1",
                      "protocol": "tcp",
                      "publicPort": "9443",
                      "localIp": "192.168.128.1",
                      "localPort": "443",
                      "allowedIps": [
                        "any"
                      ]
                    },
                    {
                      "name": "Rule 2",
                      "protocol": "tcp",
                      "publicPort": "8080",
                      "localIp": "192.168.128.1",
                      "localPort": "80",
                      "allowedIps": [
                        "10.82.110.0/24",
                        "10.82.111.0/24"
                      ]
                    }
                  ]
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/oneToManyNatRules"

        payload: dict[str, Any] = {}
        if rules is not None:
            payload["rules"] = [item.model_dump(by_alias=True, exclude_none=True) for item in rules]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceFirewallOneToManyNatRules",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def get_network_appliance_firewall_one_to_one_nat_rules(self, network_id: str) -> DictResponse:
        """Return the 1:1 NAT mapping rules for an MX network.

        [API documentation: getNetworkApplianceFirewallOneToOneNatRules](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-one-to-one-nat-rules)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "name": "Service behind NAT",
                  "lanIp": "192.168.128.22",
                  "publicIp": "146.12.3.33",
                  "uplink": "internet1",
                  "allowedInbound": [
                    {
                      "protocol": "tcp",
                      "destinationPorts": [
                        "80"
                      ],
                      "allowedIps": [
                        "10.82.112.0/24",
                        "10.82.0.0/16"
                      ]
                    },
                    {
                      "protocol": "udp",
                      "destinationPorts": [
                        "8080"
                      ],
                      "allowedIps": [
                        "10.81.110.5",
                        "10.81.0.0/16"
                      ]
                    }
                  ]
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/oneToOneNatRules"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallOneToOneNatRules",
            path=path,
            response_schema=DictResponse,
        )

    def update_network_appliance_firewall_one_to_one_nat_rules(
        self,
        *,
        network_id: str,
        rules: list[UpdateNetworkApplianceFirewallOneToOneNatRulesRulesItem],
    ) -> DictResponse:
        """Set the 1:1 NAT mapping rules for an MX network.

        [API documentation: updateNetworkApplianceFirewallOneToOneNatRules](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-firewall-one-to-one-nat-rules)

        Args:
            network_id: Network ID.
            rules: An array of 1:1 nat rules.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "name": "Service behind NAT",
                  "lanIp": "192.168.128.22",
                  "publicIp": "146.12.3.33",
                  "uplink": "internet1",
                  "allowedInbound": [
                    {
                      "protocol": "tcp",
                      "destinationPorts": [
                        "80"
                      ],
                      "allowedIps": [
                        "10.82.112.0/24",
                        "10.82.0.0/16"
                      ]
                    },
                    {
                      "protocol": "udp",
                      "destinationPorts": [
                        "8080"
                      ],
                      "allowedIps": [
                        "10.81.110.5",
                        "10.81.0.0/16"
                      ]
                    }
                  ]
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/oneToOneNatRules"

        payload: dict[str, Any] = {}
        if rules is not None:
            payload["rules"] = [item.model_dump(by_alias=True, exclude_none=True) for item in rules]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceFirewallOneToOneNatRules",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def get_network_appliance_firewall_port_forwarding_rules(
        self, network_id: str
    ) -> GetNetworkApplianceFirewallPortForwardingRulesResponse:
        """Return the port forwarding rules for an MX network.

        [API documentation: getNetworkApplianceFirewallPortForwardingRules](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-port-forwarding-rules)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "lanIp": "192.168.128.1",
                  "allowedIps": [
                    "any"
                  ],
                  "name": "Description of Port Forwarding Rule",
                  "protocol": "tcp",
                  "publicPort": "8100-8101",
                  "localPort": "442-443",
                  "uplink": "both"
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/portForwardingRules"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallPortForwardingRules",
            path=path,
            response_schema=GetNetworkApplianceFirewallPortForwardingRulesResponse,
        )

    def update_network_appliance_firewall_port_forwarding_rules(
        self,
        *,
        network_id: str,
        rules: list[UpdateNetworkApplianceFirewallPortForwardingRulesRulesItem],
    ) -> UpdateNetworkApplianceFirewallPortForwardingRulesResponse:
        """Update the port forwarding rules for an MX network.

        [API documentation: updateNetworkApplianceFirewallPortForwardingRules](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-firewall-port-forwarding-rules)

        Args:
            network_id: Network ID.
            rules: An array of port forwarding params.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "lanIp": "192.168.128.1",
                  "allowedIps": [
                    "any"
                  ],
                  "name": "Description of Port Forwarding Rule",
                  "protocol": "tcp",
                  "publicPort": "8100-8101",
                  "localPort": "442-443",
                  "uplink": "both"
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/portForwardingRules"

        payload: dict[str, Any] = {}
        if rules is not None:
            payload["rules"] = [item.model_dump(by_alias=True, exclude_none=True) for item in rules]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceFirewallPortForwardingRules",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceFirewallPortForwardingRulesResponse,
        )

    def get_network_appliance_firewall_settings(self, network_id: str) -> DictResponse:
        """Return the firewall settings for this network.

        [API documentation: getNetworkApplianceFirewallSettings](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-firewall-settings)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "spoofingProtection": {
                "ipSourceGuard": {
                  "mode": "block"
                }
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/settings"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceFirewallSettings",
            path=path,
            response_schema=DictResponse,
        )

    def update_network_appliance_firewall_settings(
        self,
        network_id: str,
        *,
        spoofing_protection: UpdateNetworkApplianceFirewallSettingsSpoofingProtection | None = None,
    ) -> DictResponse:
        """Update the firewall settings for this network.

        [API documentation: updateNetworkApplianceFirewallSettings](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-firewall-settings)

        Args:
            network_id: Network ID.
            spoofing_protection: Spoofing protection settings.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "spoofingProtection": {
                "ipSourceGuard": {
                  "mode": "block"
                }
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/firewall/settings"

        payload: dict[str, Any] = {}
        if spoofing_protection is not None:
            payload["spoofingProtection"] = spoofing_protection.model_dump(
                by_alias=True, exclude_none=True
            )

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceFirewallSettings",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def get_network_appliance_ports(
        self, network_id: str
    ) -> PaginatedResponse[GetNetworkAppliancePortsResponseItem]:
        """List per-port VLAN settings for all ports of a MX.

        [API documentation: getNetworkAppliancePorts](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-ports)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "number": 1,
                "enabled": true,
                "type": "access",
                "dropUntaggedTraffic": false,
                "vlan": 3,
                "allowedVlans": "all",
                "accessPolicy": "open"
              }
            ]
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/ports"

        return self._session.get_pages(
            scope="appliance",
            operation_id="getNetworkAppliancePorts",
            path=path,
            item_schema=GetNetworkAppliancePortsResponseItem,
        )

    def get_network_appliance_port(
        self, *, network_id: str, port_id: str
    ) -> GetNetworkAppliancePortResponse:
        """Return per-port VLAN settings for a single MX port.

        [API documentation: getNetworkAppliancePort](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-port)

        Args:
            network_id: Network ID.
            port_id: Port ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "number": 1,
              "enabled": true,
              "type": "access",
              "dropUntaggedTraffic": false,
              "vlan": 3,
              "allowedVlans": "all",
              "accessPolicy": "open"
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        port_id = urllib.parse.quote(str(port_id), safe="")
        path = f"/networks/{network_id}/appliance/ports/{port_id}"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkAppliancePort",
            path=path,
            response_schema=GetNetworkAppliancePortResponse,
        )

    def update_network_appliance_port(
        self,
        *,
        network_id: str,
        port_id: str,
        enabled: bool | None = None,
        drop_untagged_traffic: bool | None = None,
        type_: str | None = None,
        vlan: int | None = None,
        allowed_vlans: str | None = None,
        access_policy: str | None = None,
    ) -> UpdateNetworkAppliancePortResponse:
        """Update the per-port VLAN settings for a single MX port.

        [API documentation: updateNetworkAppliancePort](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-port)

        Args:
            network_id: Network ID.
            port_id: Port ID.
            enabled: The status of the port.
            drop_untagged_traffic: Trunk port can Drop all Untagged traffic. When true, no VLAN is
                required. Access ports cannot have dropUntaggedTraffic set to true.
            type_: The type of the port: 'access' or 'trunk'.
            vlan: Native VLAN when the port is in Trunk mode. Access VLAN when the port is in Access
                mode.
            allowed_vlans: Comma-delimited list of the VLAN ID's allowed on the port, or 'all' to
                permit all VLAN's on the port.
            access_policy: The name of the policy. Only applicable to Access ports. Valid values
                are: 'open', '8021x-radius', 'mac-radius', 'hybris-radius' for MX64 or
                Z3 or any MX supporting the per port authentication feature. Otherwise,
                'open' is the only valid value and 'open' is the default value if the
                field is missing.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "number": 1,
              "enabled": true,
              "type": "access",
              "dropUntaggedTraffic": false,
              "vlan": 3,
              "allowedVlans": "all",
              "accessPolicy": "open"
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        port_id = urllib.parse.quote(str(port_id), safe="")
        path = f"/networks/{network_id}/appliance/ports/{port_id}"

        payload: dict[str, Any] = {}
        if enabled is not None:
            payload["enabled"] = enabled
        if drop_untagged_traffic is not None:
            payload["dropUntaggedTraffic"] = drop_untagged_traffic
        if type_ is not None:
            payload["type"] = type_
        if vlan is not None:
            payload["vlan"] = vlan
        if allowed_vlans is not None:
            payload["allowedVlans"] = allowed_vlans
        if access_policy is not None:
            payload["accessPolicy"] = access_policy

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkAppliancePort",
            path=path,
            json=payload,
            response_schema=UpdateNetworkAppliancePortResponse,
        )

    def get_network_appliance_prefixes_delegated_statics(
        self, network_id: str
    ) -> PaginatedResponse[GetNetworkAppliancePrefixesDelegatedStaticsResponseItem]:
        """List static delegated prefixes for a network.

        [API documentation: getNetworkAppliancePrefixesDelegatedStatics](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-prefixes-delegated-statics)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "staticDelegatedPrefixId": "1284392014819",
                "prefix": "2001:db8:3c4d:15::/64",
                "origin": {
                  "type": "internet",
                  "interfaces": [
                    "wan1"
                  ]
                },
                "description": "Prefix on WAN 1 of Long Island Office network",
                "createdAt": "2018-05-12T00:00:00Z",
                "updatedAt": "2018-05-12T00:00:00Z"
              }
            ]
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/prefixes/delegated/statics"

        return self._session.get_pages(
            scope="appliance",
            operation_id="getNetworkAppliancePrefixesDelegatedStatics",
            path=path,
            item_schema=GetNetworkAppliancePrefixesDelegatedStaticsResponseItem,
        )

    def create_network_appliance_prefixes_delegated_static(
        self,
        *,
        network_id: str,
        prefix: str,
        origin: CreateNetworkAppliancePrefixesDelegatedStaticOrigin,
        description: str | None = None,
    ) -> DictResponse:
        """Add a static delegated prefix from a network.

        [API documentation: createNetworkAppliancePrefixesDelegatedStatic](https://developer.cisco.com/meraki/api-v1/#!create-network-appliance-prefixes-delegated-static)

        Args:
            network_id: Network ID.
            prefix: A static IPv6 prefix.
            origin: The origin of the prefix.
            description: A name or description for the prefix.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "staticDelegatedPrefixId": "1284392014819",
              "prefix": "2001:db8:3c4d:15::/64",
              "origin": {
                "type": "internet",
                "interfaces": [
                  "wan1"
                ]
              },
              "description": "Prefix on WAN 1 of Long Island Office network",
              "createdAt": "2018-05-12T00:00:00Z",
              "updatedAt": "2018-05-12T00:00:00Z"
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/prefixes/delegated/statics"

        payload: dict[str, Any] = {}
        if prefix is not None:
            payload["prefix"] = prefix
        if origin is not None:
            payload["origin"] = origin.model_dump(by_alias=True, exclude_none=True)
        if description is not None:
            payload["description"] = description

        return self._session.post(
            scope="appliance",
            operation_id="createNetworkAppliancePrefixesDelegatedStatic",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def get_network_appliance_prefixes_delegated_static(
        self, *, network_id: str, static_delegated_prefix_id: str
    ) -> GetNetworkAppliancePrefixesDelegatedStaticResponse:
        """Return a static delegated prefix from a network.

        [API documentation: getNetworkAppliancePrefixesDelegatedStatic](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-prefixes-delegated-static)

        Args:
            network_id: Network ID.
            static_delegated_prefix_id: Static delegated prefix ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "staticDelegatedPrefixId": "1284392014819",
              "prefix": "2001:db8:3c4d:15::/64",
              "origin": {
                "type": "internet",
                "interfaces": [
                  "wan1"
                ]
              },
              "description": "Prefix on WAN 1 of Long Island Office network",
              "createdAt": "2018-05-12T00:00:00Z",
              "updatedAt": "2018-05-12T00:00:00Z"
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        static_delegated_prefix_id = urllib.parse.quote(str(static_delegated_prefix_id), safe="")
        path = f"/networks/{network_id}/appliance/prefixes/delegated/statics/{static_delegated_prefix_id}"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkAppliancePrefixesDelegatedStatic",
            path=path,
            response_schema=GetNetworkAppliancePrefixesDelegatedStaticResponse,
        )

    def update_network_appliance_prefixes_delegated_static(
        self,
        *,
        network_id: str,
        static_delegated_prefix_id: str,
        prefix: str | None = None,
        origin: UpdateNetworkAppliancePrefixesDelegatedStaticOrigin | None = None,
        description: str | None = None,
    ) -> DictResponse:
        """Update a static delegated prefix from a network.

        [API documentation: updateNetworkAppliancePrefixesDelegatedStatic](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-prefixes-delegated-static)

        Args:
            network_id: Network ID.
            static_delegated_prefix_id: Static delegated prefix ID.
            prefix: A static IPv6 prefix.
            origin: The origin of the prefix.
            description: A name or description for the prefix.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "staticDelegatedPrefixId": "1284392014819",
              "prefix": "2001:db8:3c4d:15::/64",
              "origin": {
                "type": "internet",
                "interfaces": [
                  "wan1"
                ]
              },
              "description": "Prefix on WAN 1 of Long Island Office network",
              "createdAt": "2018-05-12T00:00:00Z",
              "updatedAt": "2018-05-12T00:00:00Z"
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        static_delegated_prefix_id = urllib.parse.quote(str(static_delegated_prefix_id), safe="")
        path = f"/networks/{network_id}/appliance/prefixes/delegated/statics/{static_delegated_prefix_id}"

        payload: dict[str, Any] = {}
        if prefix is not None:
            payload["prefix"] = prefix
        if origin is not None:
            payload["origin"] = origin.model_dump(by_alias=True, exclude_none=True)
        if description is not None:
            payload["description"] = description

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkAppliancePrefixesDelegatedStatic",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def delete_network_appliance_prefixes_delegated_static(
        self, *, network_id: str, static_delegated_prefix_id: str
    ) -> None:
        """Delete a static delegated prefix from a network.

        [API documentation: deleteNetworkAppliancePrefixesDelegatedStatic](https://developer.cisco.com/meraki/api-v1/#!delete-network-appliance-prefixes-delegated-static)

        Args:
            network_id: Network ID.
            static_delegated_prefix_id: Static delegated prefix ID.

        Returns:
            Successful operation.

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        static_delegated_prefix_id = urllib.parse.quote(str(static_delegated_prefix_id), safe="")
        path = f"/networks/{network_id}/appliance/prefixes/delegated/statics/{static_delegated_prefix_id}"

        return self._session.delete(
            scope="appliance",
            operation_id="deleteNetworkAppliancePrefixesDelegatedStatic",
            path=path,
        )

    def get_network_appliance_rf_profiles(
        self, network_id: str
    ) -> GetNetworkApplianceRfProfilesResponse:
        """List the RF profiles for this network.

        [API documentation: getNetworkApplianceRfProfiles](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-rf-profiles)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "assigned": [
                {
                  "id": "1234",
                  "networkId": "N_24329156",
                  "name": "Some Custom RF Profile",
                  "twoFourGhzSettings": {
                    "minBitrate": 11.0,
                    "axEnabled": true
                  },
                  "fiveGhzSettings": {
                    "minBitrate": 12,
                    "axEnabled": true
                  },
                  "perSsidSettings": {
                    "1": {
                      "bandOperationMode": "dual",
                      "bandSteeringEnabled": true
                    },
                    "2": {
                      "bandOperationMode": "dual",
                      "bandSteeringEnabled": true
                    },
                    "3": {
                      "bandOperationMode": "dual",
                      "bandSteeringEnabled": true
                    },
                    "4": {
                      "bandOperationMode": "dual",
                      "bandSteeringEnabled": true
                    }
                  }
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/rfProfiles"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceRfProfiles",
            path=path,
            response_schema=GetNetworkApplianceRfProfilesResponse,
        )

    def create_network_appliance_rf_profile(
        self,
        *,
        network_id: str,
        name: str,
        two_four_ghz_settings: CreateNetworkApplianceRfProfileTwoFourGhzSettings | None = None,
        five_ghz_settings: CreateNetworkApplianceRfProfileFiveGhzSettings | None = None,
        per_ssid_settings: CreateNetworkApplianceRfProfilePerSsidSettings | None = None,
    ) -> CreateNetworkApplianceRfProfileResponse:
        """Creates new RF profile for this network.

        [API documentation: createNetworkApplianceRfProfile](https://developer.cisco.com/meraki/api-v1/#!create-network-appliance-rf-profile)

        Args:
            network_id: Network ID.
            name: The name of the new profile. Must be unique. This param is required on creation.
            two_four_ghz_settings: Settings related to 2.4Ghz band.
            five_ghz_settings: Settings related to 5Ghz band.
            per_ssid_settings: Per-SSID radio settings by number.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "id": "1234",
              "networkId": "N_24329156",
              "name": "Some Custom RF Profile",
              "twoFourGhzSettings": {
                "minBitrate": 11.0,
                "axEnabled": true
              },
              "fiveGhzSettings": {
                "minBitrate": 12,
                "axEnabled": true
              },
              "perSsidSettings": {
                "1": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                },
                "2": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                },
                "3": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                },
                "4": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                }
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/rfProfiles"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name
        if two_four_ghz_settings is not None:
            payload["twoFourGhzSettings"] = two_four_ghz_settings.model_dump(
                by_alias=True, exclude_none=True
            )
        if five_ghz_settings is not None:
            payload["fiveGhzSettings"] = five_ghz_settings.model_dump(
                by_alias=True, exclude_none=True
            )
        if per_ssid_settings is not None:
            payload["perSsidSettings"] = per_ssid_settings.model_dump(
                by_alias=True, exclude_none=True
            )

        return self._session.post(
            scope="appliance",
            operation_id="createNetworkApplianceRfProfile",
            path=path,
            json=payload,
            response_schema=CreateNetworkApplianceRfProfileResponse,
        )

    def get_network_appliance_rf_profile(
        self, *, network_id: str, rf_profile_id: str
    ) -> GetNetworkApplianceRfProfileResponse:
        """Return a RF profile.

        [API documentation: getNetworkApplianceRfProfile](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-rf-profile)

        Args:
            network_id: Network ID.
            rf_profile_id: Rf profile ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "id": "1234",
              "networkId": "N_24329156",
              "name": "Some Custom RF Profile",
              "twoFourGhzSettings": {
                "minBitrate": 11.0,
                "axEnabled": true
              },
              "fiveGhzSettings": {
                "minBitrate": 12,
                "axEnabled": true
              },
              "perSsidSettings": {
                "1": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                },
                "2": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                },
                "3": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                },
                "4": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                }
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        rf_profile_id = urllib.parse.quote(str(rf_profile_id), safe="")
        path = f"/networks/{network_id}/appliance/rfProfiles/{rf_profile_id}"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceRfProfile",
            path=path,
            response_schema=GetNetworkApplianceRfProfileResponse,
        )

    def update_network_appliance_rf_profile(
        self,
        *,
        network_id: str,
        rf_profile_id: str,
        name: str | None = None,
        two_four_ghz_settings: UpdateNetworkApplianceRfProfileTwoFourGhzSettings | None = None,
        five_ghz_settings: UpdateNetworkApplianceRfProfileFiveGhzSettings | None = None,
        per_ssid_settings: UpdateNetworkApplianceRfProfilePerSsidSettings | None = None,
    ) -> UpdateNetworkApplianceRfProfileResponse:
        """Updates specified RF profile for this network.

        [API documentation: updateNetworkApplianceRfProfile](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-rf-profile)

        Args:
            network_id: Network ID.
            rf_profile_id: Rf profile ID.
            name: The name of the new profile. Must be unique.
            two_four_ghz_settings: Settings related to 2.4Ghz band.
            five_ghz_settings: Settings related to 5Ghz band.
            per_ssid_settings: Per-SSID radio settings by number.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "id": "1234",
              "networkId": "N_24329156",
              "name": "Some Custom RF Profile",
              "twoFourGhzSettings": {
                "minBitrate": 11.0,
                "axEnabled": true
              },
              "fiveGhzSettings": {
                "minBitrate": 12,
                "axEnabled": true
              },
              "perSsidSettings": {
                "1": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                },
                "2": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                },
                "3": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                },
                "4": {
                  "bandOperationMode": "dual",
                  "bandSteeringEnabled": true
                }
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        rf_profile_id = urllib.parse.quote(str(rf_profile_id), safe="")
        path = f"/networks/{network_id}/appliance/rfProfiles/{rf_profile_id}"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name
        if two_four_ghz_settings is not None:
            payload["twoFourGhzSettings"] = two_four_ghz_settings.model_dump(
                by_alias=True, exclude_none=True
            )
        if five_ghz_settings is not None:
            payload["fiveGhzSettings"] = five_ghz_settings.model_dump(
                by_alias=True, exclude_none=True
            )
        if per_ssid_settings is not None:
            payload["perSsidSettings"] = per_ssid_settings.model_dump(
                by_alias=True, exclude_none=True
            )

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceRfProfile",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceRfProfileResponse,
        )

    def delete_network_appliance_rf_profile(self, *, network_id: str, rf_profile_id: str) -> None:
        """Delete a RF Profile.

        [API documentation: deleteNetworkApplianceRfProfile](https://developer.cisco.com/meraki/api-v1/#!delete-network-appliance-rf-profile)

        Args:
            network_id: Network ID.
            rf_profile_id: Rf profile ID.

        Returns:
            Successful operation.

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        rf_profile_id = urllib.parse.quote(str(rf_profile_id), safe="")
        path = f"/networks/{network_id}/appliance/rfProfiles/{rf_profile_id}"

        return self._session.delete(
            scope="appliance", operation_id="deleteNetworkApplianceRfProfile", path=path
        )

    def update_network_appliance_sdwan_internet_policies(
        self,
        network_id: str,
        *,
        wan_traffic_uplink_preferences: list[
            UpdateNetworkApplianceSdwanInternetPoliciesWanTrafficUplinkPreferencesItem
        ]
        | None = None,
    ) -> UpdateNetworkApplianceSdwanInternetPoliciesResponse:
        """Update SDWAN internet traffic preferences for an MX network.

        [API documentation: updateNetworkApplianceSdwanInternetPolicies](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-sdwan-internet-policies)

        Args:
            network_id: Network ID.
            wan_traffic_uplink_preferences: policies with respective traffic filters for an MX
                network.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "wanTrafficUplinkPreferences": [
                {
                  "preferredUplink": "wan1",
                  "failOverCriterion": "poorPerformance",
                  "performanceClass": {
                    "type": "custom",
                    "builtinPerformanceClassName": "VoIP",
                    "customPerformanceClassId": "123456"
                  },
                  "trafficFilters": [
                    {
                      "type": "custom",
                      "value": {
                        "protocol": "tcp",
                        "source": {
                          "port": "1-1024",
                          "cidr": "192.168.1.0/24",
                          "vlan": 10,
                          "host": 254
                        },
                        "destination": {
                          "port": "any",
                          "cidr": "any",
                          "applications": [
                            {
                              "id": "meraki:layer7/application/3",
                              "name": "DNS",
                              "type": "major"
                            }
                          ]
                        }
                      }
                    }
                  ]
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/sdwan/internetPolicies"

        payload: dict[str, Any] = {}
        if wan_traffic_uplink_preferences is not None:
            payload["wanTrafficUplinkPreferences"] = [
                item.model_dump(by_alias=True, exclude_none=True)
                for item in wan_traffic_uplink_preferences
            ]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceSdwanInternetPolicies",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceSdwanInternetPoliciesResponse,
        )

    def get_network_appliance_security_events(
        self,
        network_id: str,
        *,
        t0: str | None = None,
        t1: str | None = None,
        timespan: float | None = None,
        per_page: int | None = None,
        starting_after: str | None = None,
        ending_before: str | None = None,
        sort_order: GetNetworkApplianceSecurityEventsSortOrder | None = None,
        total_pages: int | Literal["all"] = "all",
        direction: Literal["prev", "next"] = "next",
    ) -> PaginatedResponse[GetNetworkApplianceSecurityEventsResponse]:
        """List the security events for a network.

        [API documentation: getNetworkApplianceSecurityEvents](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-security-events)

        Args:
            network_id: Network ID.
            t0: The beginning of the timespan for the data. Data is gathered after the specified t0
                value. The maximum lookback period is 365 days from today.
            t1: The end of the timespan for the data. t1 can be a maximum of 365 days after t0.
            timespan: The timespan for which the information will be fetched. If specifying
                timespan, do not specify parameters t0 and t1. The value must be in
                seconds and be less than or equal to 365 days. The default is 31 days.
            per_page: The number of entries per page returned. Acceptable range is 3 - 1000. Default
                is 100.
            starting_after: A token used by the server to indicate the start of the page. Often this
                is a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            ending_before: A token used by the server to indicate the end of the page. Often this is
                a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            sort_order: Sorted order of security events based on event detection time. Order options
                are 'ascending' or 'descending'. Default is ascending order.
            total_pages: use with per_page to get total results up to total_pages * per_page; -1 or
                "all" for all pages.
            direction: direction to paginate, either "next" (default) or "prev" page.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "ts": "2018-02-11T00:00:00.090210Z",
                "eventType": "File Scanned",
                "clientName": "COMPUTER-M-V78J",
                "clientMac": "10:dd:b1:eb:88:f8",
                "clientIp": "192.168.128.2",
                "srcIp": "192.168.128.2",
                "destIp": "119.192.233.48",
                "protocol": "http",
                "uri": "http://www.favorite-icons.com/program/FavoriteIconsUninstall.exe",
                "canonicalName": "PUA.Win.Dropper.Kraddare::1201",
                "destinationPort": 80,
                "fileHash": "3ec1b9a95fe62aa25fc959643a0f227b76d253094681934daaf628d3574b3463",
                "fileType": "MS_EXE",
                "fileSizeBytes": 193688,
                "disposition": "Malicious",
                "action": "Blocked"
              },
              {
                "ts": "2018-02-11T00:00:00.090210Z",
                "eventType": "IDS Alert",
                "deviceMac": "00:18:0a:01:02:03",
                "clientMac": "A1:B2:C3:D4:E5:F6",
                "srcIp": "1.2.3.4:34195",
                "destIp": "10.20.30.40:80",
                "protocol": "tcp/ip",
                "priority": "2",
                "classification": "4",
                "blocked": true,
                "message": "SERVER-WEBAPP JBoss JMX console access attempt",
                "signature": "1:21516:9",
                "sigSource": "",
                "ruleId": "meraki:intrusion/snort/GID/1/SID/26267"
              }
            ]
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/security/events"

        params: dict[str, Any] = {}
        if t0 is not None:
            params["t0"] = t0
        if t1 is not None:
            params["t1"] = t1
        if timespan is not None:
            params["timespan"] = timespan
        if per_page is not None:
            params["perPage"] = per_page
        if starting_after is not None:
            params["startingAfter"] = starting_after
        if ending_before is not None:
            params["endingBefore"] = ending_before
        if sort_order is not None:
            params["sortOrder"] = sort_order

        return self._session.get_pages(
            scope="appliance",
            operation_id="getNetworkApplianceSecurityEvents",
            path=path,
            params=params,
            total_pages=total_pages,
            direction=direction,
            item_schema=GetNetworkApplianceSecurityEventsResponse,
        )

    def get_network_appliance_security_intrusion(
        self, network_id: str
    ) -> GetNetworkApplianceSecurityIntrusionResponse:
        """Returns all supported intrusion settings for an MX network.

        [API documentation: getNetworkApplianceSecurityIntrusion](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-security-intrusion)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "mode": "prevention",
              "idsRulesets": "balanced",
              "protectedNetworks": {
                "useDefault": false,
                "includedCidr": [
                  "10.0.0.0/8",
                  "127.0.0.0/8",
                  "169.254.0.0/16",
                  "172.16.0.0/12"
                ],
                "excludedCidr": [
                  "10.0.0.0/8",
                  "127.0.0.0/8"
                ]
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/security/intrusion"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceSecurityIntrusion",
            path=path,
            response_schema=GetNetworkApplianceSecurityIntrusionResponse,
        )

    def update_network_appliance_security_intrusion(
        self,
        network_id: str,
        *,
        mode: UpdateNetworkApplianceSecurityIntrusionMode | None = None,
        ids_rulesets: UpdateNetworkApplianceSecurityIntrusionIdsRulesets | None = None,
        protected_networks: UpdateNetworkApplianceSecurityIntrusionProtectedNetworks | None = None,
    ) -> UpdateNetworkApplianceSecurityIntrusionResponse:
        """Set the supported intrusion settings for an MX network.

        [API documentation: updateNetworkApplianceSecurityIntrusion](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-security-intrusion)

        Args:
            network_id: Network ID.
            mode: Set mode to 'disabled'/'detection'/'prevention' (optional - omitting will leave
                current config unchanged).
            ids_rulesets: Set the detection ruleset 'connectivity'/'balanced'/'security' (optional -
                omitting will leave current config unchanged). Default value is
                'balanced' if none currently saved.
            protected_networks: Set the included/excluded networks from the intrusion engine
                (optional - omitting will leave current config unchanged). This is
                available only in 'passthrough' mode.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "mode": "prevention",
              "idsRulesets": "balanced",
              "protectedNetworks": {
                "useDefault": false,
                "includedCidr": [
                  "10.0.0.0/8",
                  "127.0.0.0/8",
                  "169.254.0.0/16",
                  "172.16.0.0/12"
                ],
                "excludedCidr": [
                  "10.0.0.0/8",
                  "127.0.0.0/8"
                ]
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/security/intrusion"

        payload: dict[str, Any] = {}
        if mode is not None:
            payload["mode"] = mode
        if ids_rulesets is not None:
            payload["idsRulesets"] = ids_rulesets
        if protected_networks is not None:
            payload["protectedNetworks"] = protected_networks.model_dump(
                by_alias=True, exclude_none=True
            )

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceSecurityIntrusion",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceSecurityIntrusionResponse,
        )

    def get_network_appliance_security_malware(
        self, network_id: str
    ) -> GetNetworkApplianceSecurityMalwareResponse:
        """Returns all supported malware settings for an MX network.

        [API documentation: getNetworkApplianceSecurityMalware](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-security-malware)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "mode": "enabled",
              "allowedUrls": [
                {
                  "url": "help.com.au",
                  "comment": "allow help.com.au"
                }
              ],
              "allowedFiles": [
                {
                  "sha256": "e82c5f7d75004727e1f3b94426b9a11c8bc4c312a9170ac9a73abace40aef503",
                  "comment": "allow ZIP file"
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/security/malware"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceSecurityMalware",
            path=path,
            response_schema=GetNetworkApplianceSecurityMalwareResponse,
        )

    def update_network_appliance_security_malware(
        self,
        *,
        network_id: str,
        mode: UpdateNetworkApplianceSecurityMalwareMode,
        allowed_urls: list[UpdateNetworkApplianceSecurityMalwareAllowedUrlsItem] | None = None,
        allowed_files: list[UpdateNetworkApplianceSecurityMalwareAllowedFilesItem] | None = None,
    ) -> UpdateNetworkApplianceSecurityMalwareResponse:
        """Set the supported malware settings for an MX network.

        [API documentation: updateNetworkApplianceSecurityMalware](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-security-malware)

        Args:
            network_id: Network ID.
            mode: Set mode to 'enabled' to enable malware prevention, otherwise 'disabled'.
            allowed_urls: The urls that should be permitted by the malware detection engine. If
                omitted, the current config will remain unchanged. This is available
                only if your network supports AMP allow listing.
            allowed_files: The sha256 digests of files that should be permitted by the malware
                detection engine. If omitted, the current config will remain unchanged.
                This is available only if your network supports AMP allow listing.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "mode": "enabled",
              "allowedUrls": [
                {
                  "url": "help.com.au",
                  "comment": "allow help.com.au"
                }
              ],
              "allowedFiles": [
                {
                  "sha256": "e82c5f7d75004727e1f3b94426b9a11c8bc4c312a9170ac9a73abace40aef503",
                  "comment": "allow ZIP file"
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/security/malware"

        payload: dict[str, Any] = {}
        if mode is not None:
            payload["mode"] = mode
        if allowed_urls is not None:
            payload["allowedUrls"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in allowed_urls
            ]
        if allowed_files is not None:
            payload["allowedFiles"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in allowed_files
            ]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceSecurityMalware",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceSecurityMalwareResponse,
        )

    def get_network_appliance_settings(
        self, network_id: str
    ) -> GetNetworkApplianceSettingsResponse:
        """Return the appliance settings for a network.

        [API documentation: getNetworkApplianceSettings](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-settings)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "clientTrackingMethod": "MAC address",
              "deploymentMode": "routed",
              "dynamicDns": {
                "enabled": true,
                "prefix": "test",
                "url": "test-adfgnnhjea.meraki.com"
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/settings"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceSettings",
            path=path,
            response_schema=GetNetworkApplianceSettingsResponse,
        )

    def update_network_appliance_settings(
        self,
        network_id: str,
        *,
        client_tracking_method: UpdateNetworkApplianceSettingsClientTrackingMethod | None = None,
        deployment_mode: UpdateNetworkApplianceSettingsDeploymentMode | None = None,
        dynamic_dns: UpdateNetworkApplianceSettingsDynamicDns | None = None,
    ) -> UpdateNetworkApplianceSettingsResponse:
        """Update the appliance settings for a network.

        [API documentation: updateNetworkApplianceSettings](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-settings)

        Args:
            network_id: Network ID.
            client_tracking_method: Client tracking method of a network.
            deployment_mode: Deployment mode of a network.
            dynamic_dns: Dynamic DNS settings for a network.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "clientTrackingMethod": "MAC address",
              "deploymentMode": "routed",
              "dynamicDns": {
                "enabled": true,
                "prefix": "test",
                "url": "test-adfgnnhjea.meraki.com"
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/settings"

        payload: dict[str, Any] = {}
        if client_tracking_method is not None:
            payload["clientTrackingMethod"] = client_tracking_method
        if deployment_mode is not None:
            payload["deploymentMode"] = deployment_mode
        if dynamic_dns is not None:
            payload["dynamicDns"] = dynamic_dns.model_dump(by_alias=True, exclude_none=True)

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceSettings",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceSettingsResponse,
        )

    def get_network_appliance_single_lan(
        self, network_id: str
    ) -> GetNetworkApplianceSingleLanResponse:
        """Return single LAN configuration.

        [API documentation: getNetworkApplianceSingleLan](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-single-lan)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "subnet": "192.168.1.0/24",
              "applianceIp": "192.168.1.2",
              "mandatoryDhcp": {
                "enabled": true
              },
              "ipv6": {
                "enabled": true,
                "prefixAssignments": [
                  {
                    "autonomous": false,
                    "staticPrefix": "2001:db8:3c4d:15::/64",
                    "staticApplianceIp6": "2001:db8:3c4d:15::1",
                    "origin": {
                      "type": "internet",
                      "interfaces": [
                        "wan0"
                      ]
                    }
                  }
                ]
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/singleLan"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceSingleLan",
            path=path,
            response_schema=GetNetworkApplianceSingleLanResponse,
        )

    def update_network_appliance_single_lan(
        self,
        network_id: str,
        *,
        subnet: str | None = None,
        appliance_ip: str | None = None,
        ipv6: UpdateNetworkApplianceSingleLanIpv6 | None = None,
        mandatory_dhcp: UpdateNetworkApplianceSingleLanMandatoryDhcp | None = None,
    ) -> UpdateNetworkApplianceSingleLanResponse:
        """Update single LAN configuration.

        [API documentation: updateNetworkApplianceSingleLan](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-single-lan)

        Args:
            network_id: Network ID.
            subnet: The subnet of the single LAN configuration.
            appliance_ip: The appliance IP address of the single LAN.
            ipv6: IPv6 configuration on the VLAN.
            mandatory_dhcp: Mandatory DHCP will enforce that clients connecting to this LAN must use
                the IP address assigned by the DHCP server. Clients who use a static IP
                address won't be able to associate. Only available on firmware versions
                17.0 and above.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "subnet": "192.168.1.0/24",
              "applianceIp": "192.168.1.2",
              "mandatoryDhcp": {
                "enabled": true
              },
              "ipv6": {
                "enabled": true,
                "prefixAssignments": [
                  {
                    "autonomous": false,
                    "staticPrefix": "2001:db8:3c4d:15::/64",
                    "staticApplianceIp6": "2001:db8:3c4d:15::1",
                    "origin": {
                      "type": "internet",
                      "interfaces": [
                        "wan0"
                      ]
                    }
                  }
                ]
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/singleLan"

        payload: dict[str, Any] = {}
        if subnet is not None:
            payload["subnet"] = subnet
        if appliance_ip is not None:
            payload["applianceIp"] = appliance_ip
        if ipv6 is not None:
            payload["ipv6"] = ipv6.model_dump(by_alias=True, exclude_none=True)
        if mandatory_dhcp is not None:
            payload["mandatoryDhcp"] = mandatory_dhcp.model_dump(by_alias=True, exclude_none=True)

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceSingleLan",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceSingleLanResponse,
        )

    def get_network_appliance_ssids(
        self, network_id: str
    ) -> PaginatedResponse[GetNetworkApplianceSsidsResponseItem]:
        """List the MX SSIDs in a network.

        [API documentation: getNetworkApplianceSsids](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-ssids)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "number": 1,
                "name": "My SSID",
                "enabled": true,
                "defaultVlanId": 1,
                "authMode": "8021x-radius",
                "radiusServers": [
                  {
                    "host": "0.0.0.0",
                    "port": 1000
                  }
                ],
                "encryptionMode": "wpa",
                "wpaEncryptionMode": "WPA2 only",
                "visible": true
              }
            ]
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/ssids"

        return self._session.get_pages(
            scope="appliance",
            operation_id="getNetworkApplianceSsids",
            path=path,
            item_schema=GetNetworkApplianceSsidsResponseItem,
        )

    def get_network_appliance_ssid(
        self, *, network_id: str, number: str
    ) -> GetNetworkApplianceSsidResponse:
        """Return a single MX SSID.

        [API documentation: getNetworkApplianceSsid](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-ssid)

        Args:
            network_id: Network ID.
            number: Number.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "number": 1,
              "name": "My SSID",
              "enabled": true,
              "defaultVlanId": 1,
              "authMode": "8021x-radius",
              "radiusServers": [
                {
                  "host": "0.0.0.0",
                  "port": 1000
                }
              ],
              "encryptionMode": "wpa",
              "wpaEncryptionMode": "WPA2 only",
              "visible": true
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        number = urllib.parse.quote(str(number), safe="")
        path = f"/networks/{network_id}/appliance/ssids/{number}"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceSsid",
            path=path,
            response_schema=GetNetworkApplianceSsidResponse,
        )

    def update_network_appliance_ssid(
        self,
        *,
        network_id: str,
        number: str,
        name: str | None = None,
        enabled: bool | None = None,
        default_vlan_id: int | None = None,
        auth_mode: UpdateNetworkApplianceSsidAuthMode | None = None,
        psk: str | None = None,
        radius_servers: list[UpdateNetworkApplianceSsidRadiusServersItem] | None = None,
        encryption_mode: UpdateNetworkApplianceSsidEncryptionMode | None = None,
        wpa_encryption_mode: UpdateNetworkApplianceSsidWpaEncryptionMode | None = None,
        visible: bool | None = None,
        dhcp_enforced_deauthentication: UpdateNetworkApplianceSsidDhcpEnforcedDeauthentication
        | None = None,
        dot11w: UpdateNetworkApplianceSsidDot11w | None = None,
    ) -> UpdateNetworkApplianceSsidResponse:
        """Update the attributes of an MX SSID.

        [API documentation: updateNetworkApplianceSsid](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-ssid)

        Args:
            network_id: Network ID.
            number: Number.
            name: The name of the SSID.
            enabled: Whether or not the SSID is enabled.
            default_vlan_id: The VLAN ID of the VLAN associated to this SSID. This parameter is only
                valid if the network is in routed mode.
            auth_mode: The association control method for the SSID ('open', 'psk', '8021x-meraki' or
                '8021x-radius').
            psk: The passkey for the SSID. This param is only valid if the authMode is 'psk'.
            radius_servers: The RADIUS 802.1x servers to be used for authentication. This param is
                only valid if the authMode is '8021x-radius'.
            encryption_mode: The psk encryption mode for the SSID ('wep' or 'wpa'). This param is
                only valid if the authMode is 'psk'.
            wpa_encryption_mode: The types of WPA encryption. ('WPA1 and WPA2', 'WPA2 only', 'WPA3
                Transition Mode' or 'WPA3 only'). This param is only valid if (1) the
                authMode is 'psk' & the encryptionMode is 'wpa' OR (2) the authMode is
                '8021x-meraki' OR (3) the authMode is '8021x-radius'.
            visible: Boolean indicating whether the MX should advertise or hide this SSID.
            dhcp_enforced_deauthentication: DHCP Enforced Deauthentication enables the
                disassociation of wireless clients in addition to Mandatory DHCP. This
                param is only valid on firmware versions >= MX 17.0 where the associated
                LAN has Mandatory DHCP Enabled.
            dot11w: The current setting for Protected Management Frames (802.11w).

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "number": 1,
              "name": "My SSID",
              "enabled": true,
              "defaultVlanId": 1,
              "authMode": "8021x-radius",
              "radiusServers": [
                {
                  "host": "0.0.0.0",
                  "port": 1000
                }
              ],
              "encryptionMode": "wpa",
              "wpaEncryptionMode": "WPA2 only",
              "visible": true
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        number = urllib.parse.quote(str(number), safe="")
        path = f"/networks/{network_id}/appliance/ssids/{number}"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name
        if enabled is not None:
            payload["enabled"] = enabled
        if default_vlan_id is not None:
            payload["defaultVlanId"] = default_vlan_id
        if auth_mode is not None:
            payload["authMode"] = auth_mode
        if psk is not None:
            payload["psk"] = psk
        if radius_servers is not None:
            payload["radiusServers"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in radius_servers
            ]
        if encryption_mode is not None:
            payload["encryptionMode"] = encryption_mode
        if wpa_encryption_mode is not None:
            payload["wpaEncryptionMode"] = wpa_encryption_mode
        if visible is not None:
            payload["visible"] = visible
        if dhcp_enforced_deauthentication is not None:
            payload["dhcpEnforcedDeauthentication"] = dhcp_enforced_deauthentication.model_dump(
                by_alias=True, exclude_none=True
            )
        if dot11w is not None:
            payload["dot11w"] = dot11w.model_dump(by_alias=True, exclude_none=True)

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceSsid",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceSsidResponse,
        )

    def get_network_appliance_static_routes(
        self, network_id: str
    ) -> PaginatedResponse[GetNetworkApplianceStaticRoutesResponseItem]:
        """List the static routes for an MX or teleworker network.

        [API documentation: getNetworkApplianceStaticRoutes](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-static-routes)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "id": "d7fa4948-7921-4dfa-af6b-ae8b16c20c39",
                "ipVersion": 4,
                "networkId": "N_24329156",
                "enabled": true,
                "name": "My route",
                "subnet": "192.168.1.0/24",
                "gatewayIp": "1.2.3.5",
                "fixedIpAssignments": {
                  "22:33:44:55:66:77": {
                    "ip": "1.2.3.4",
                    "name": "Some client name"
                  }
                },
                "reservedIpRanges": [
                  {
                    "start": "192.168.1.0",
                    "end": "192.168.1.1",
                    "comment": "A reserved IP range"
                  }
                ],
                "gatewayVlanId": 100
              }
            ]
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/staticRoutes"

        return self._session.get_pages(
            scope="appliance",
            operation_id="getNetworkApplianceStaticRoutes",
            path=path,
            item_schema=GetNetworkApplianceStaticRoutesResponseItem,
        )

    def create_network_appliance_static_route(
        self,
        *,
        network_id: str,
        name: str,
        subnet: str,
        gateway_ip: str,
        gateway_vlan_id: str | None = None,
    ) -> CreateNetworkApplianceStaticRouteResponse:
        """Add a static route for an MX or teleworker network.

        [API documentation: createNetworkApplianceStaticRoute](https://developer.cisco.com/meraki/api-v1/#!create-network-appliance-static-route)

        Args:
            network_id: Network ID.
            name: Name of the route.
            subnet: Subnet of the route.
            gateway_ip: Gateway IP address (next hop).
            gateway_vlan_id: Gateway VLAN ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "id": "d7fa4948-7921-4dfa-af6b-ae8b16c20c39",
              "ipVersion": 4,
              "networkId": "N_24329156",
              "enabled": true,
              "name": "My route",
              "subnet": "192.168.1.0/24",
              "gatewayIp": "1.2.3.5",
              "fixedIpAssignments": {
                "22:33:44:55:66:77": {
                  "ip": "1.2.3.4",
                  "name": "Some client name"
                }
              },
              "reservedIpRanges": [
                {
                  "start": "192.168.1.0",
                  "end": "192.168.1.1",
                  "comment": "A reserved IP range"
                }
              ],
              "gatewayVlanId": 100
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/staticRoutes"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name
        if subnet is not None:
            payload["subnet"] = subnet
        if gateway_ip is not None:
            payload["gatewayIp"] = gateway_ip
        if gateway_vlan_id is not None:
            payload["gatewayVlanId"] = gateway_vlan_id

        return self._session.post(
            scope="appliance",
            operation_id="createNetworkApplianceStaticRoute",
            path=path,
            json=payload,
            response_schema=CreateNetworkApplianceStaticRouteResponse,
        )

    def get_network_appliance_static_route(
        self, *, network_id: str, static_route_id: str
    ) -> GetNetworkApplianceStaticRouteResponse:
        """Return a static route for an MX or teleworker network.

        [API documentation: getNetworkApplianceStaticRoute](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-static-route)

        Args:
            network_id: Network ID.
            static_route_id: Static route ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "id": "d7fa4948-7921-4dfa-af6b-ae8b16c20c39",
              "ipVersion": 4,
              "networkId": "N_24329156",
              "enabled": true,
              "name": "My route",
              "subnet": "192.168.1.0/24",
              "gatewayIp": "1.2.3.5",
              "fixedIpAssignments": {
                "22:33:44:55:66:77": {
                  "ip": "1.2.3.4",
                  "name": "Some client name"
                }
              },
              "reservedIpRanges": [
                {
                  "start": "192.168.1.0",
                  "end": "192.168.1.1",
                  "comment": "A reserved IP range"
                }
              ],
              "gatewayVlanId": 100
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        static_route_id = urllib.parse.quote(str(static_route_id), safe="")
        path = f"/networks/{network_id}/appliance/staticRoutes/{static_route_id}"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceStaticRoute",
            path=path,
            response_schema=GetNetworkApplianceStaticRouteResponse,
        )

    def update_network_appliance_static_route(
        self,
        *,
        network_id: str,
        static_route_id: str,
        name: str | None = None,
        subnet: str | None = None,
        gateway_ip: str | None = None,
        gateway_vlan_id: str | None = None,
        enabled: bool | None = None,
        fixed_ip_assignments: dict[str, UpdateNetworkApplianceStaticRouteFixedIpAssignmentsValue]
        | None = None,
        reserved_ip_ranges: list[UpdateNetworkApplianceStaticRouteReservedIpRangesItem]
        | None = None,
    ) -> UpdateNetworkApplianceStaticRouteResponse:
        """Update a static route for an MX or teleworker network.

        [API documentation: updateNetworkApplianceStaticRoute](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-static-route)

        Args:
            network_id: Network ID.
            static_route_id: Static route ID.
            name: Name of the route.
            subnet: Subnet of the route.
            gateway_ip: Gateway IP address (next hop).
            gateway_vlan_id: Gateway VLAN ID.
            enabled: Whether the route should be enabled or not.
            fixed_ip_assignments: Fixed DHCP IP assignments on the route.
            reserved_ip_ranges: DHCP reserved IP ranges.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "id": "d7fa4948-7921-4dfa-af6b-ae8b16c20c39",
              "ipVersion": 4,
              "networkId": "N_24329156",
              "enabled": true,
              "name": "My route",
              "subnet": "192.168.1.0/24",
              "gatewayIp": "1.2.3.5",
              "fixedIpAssignments": {
                "22:33:44:55:66:77": {
                  "ip": "1.2.3.4",
                  "name": "Some client name"
                }
              },
              "reservedIpRanges": [
                {
                  "start": "192.168.1.0",
                  "end": "192.168.1.1",
                  "comment": "A reserved IP range"
                }
              ],
              "gatewayVlanId": 100
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        static_route_id = urllib.parse.quote(str(static_route_id), safe="")
        path = f"/networks/{network_id}/appliance/staticRoutes/{static_route_id}"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name
        if subnet is not None:
            payload["subnet"] = subnet
        if gateway_ip is not None:
            payload["gatewayIp"] = gateway_ip
        if gateway_vlan_id is not None:
            payload["gatewayVlanId"] = gateway_vlan_id
        if enabled is not None:
            payload["enabled"] = enabled
        if fixed_ip_assignments is not None:
            payload["fixedIpAssignments"] = {
                k: v.model_dump(by_alias=True, exclude_none=True)
                for k, v in fixed_ip_assignments.items()
            }
        if reserved_ip_ranges is not None:
            payload["reservedIpRanges"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in reserved_ip_ranges
            ]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceStaticRoute",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceStaticRouteResponse,
        )

    def delete_network_appliance_static_route(
        self, *, network_id: str, static_route_id: str
    ) -> None:
        """Delete a static route from an MX or teleworker network.

        [API documentation: deleteNetworkApplianceStaticRoute](https://developer.cisco.com/meraki/api-v1/#!delete-network-appliance-static-route)

        Args:
            network_id: Network ID.
            static_route_id: Static route ID.

        Returns:
            Successful operation.

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        static_route_id = urllib.parse.quote(str(static_route_id), safe="")
        path = f"/networks/{network_id}/appliance/staticRoutes/{static_route_id}"

        return self._session.delete(
            scope="appliance", operation_id="deleteNetworkApplianceStaticRoute", path=path
        )

    def get_network_appliance_traffic_shaping(self, network_id: str) -> DictResponse:
        """Display the traffic shaping settings for an MX network.

        [API documentation: getNetworkApplianceTrafficShaping](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-traffic-shaping)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "globalBandwidthLimits": {
                "limitUp": 2048,
                "limitDown": 5120
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceTrafficShaping",
            path=path,
            response_schema=DictResponse,
        )

    def update_network_appliance_traffic_shaping(
        self,
        network_id: str,
        *,
        global_bandwidth_limits: UpdateNetworkApplianceTrafficShapingGlobalBandwidthLimits
        | None = None,
    ) -> DictResponse:
        """Update the traffic shaping settings for an MX network.

        [API documentation: updateNetworkApplianceTrafficShaping](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-traffic-shaping)

        Args:
            network_id: Network ID.
            global_bandwidth_limits: Global per-client bandwidth limit.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "globalBandwidthLimits": {
                "limitUp": 2048,
                "limitDown": 5120
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping"

        payload: dict[str, Any] = {}
        if global_bandwidth_limits is not None:
            payload["globalBandwidthLimits"] = global_bandwidth_limits.model_dump(
                by_alias=True, exclude_none=True
            )

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceTrafficShaping",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def get_network_appliance_traffic_shaping_custom_performance_classes(
        self, network_id: str
    ) -> PaginatedResponse[GetNetworkApplianceTrafficShapingCustomPerformanceClassesResponseItem]:
        """List all custom performance classes for an MX network.

        [API documentation: getNetworkApplianceTrafficShapingCustomPerformanceClasses](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-traffic-shaping-custom-performance-classes)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "name": "myCustomPerformanceClass",
                "customPerformanceClassId": "123",
                "maxLatency": 100,
                "maxJitter": 100,
                "maxLossPercentage": 5
              }
            ]
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/customPerformanceClasses"

        return self._session.get_pages(
            scope="appliance",
            operation_id="getNetworkApplianceTrafficShapingCustomPerformanceClasses",
            path=path,
            item_schema=GetNetworkApplianceTrafficShapingCustomPerformanceClassesResponseItem,
        )

    def create_network_appliance_traffic_shaping_custom_performance_class(
        self,
        *,
        network_id: str,
        name: str,
        max_latency: int | None = None,
        max_jitter: int | None = None,
        max_loss_percentage: int | None = None,
    ) -> CreateNetworkApplianceTrafficShapingCustomPerformanceClassResponse:
        """Add a custom performance class for an MX network.

        [API documentation: createNetworkApplianceTrafficShapingCustomPerformanceClass](https://developer.cisco.com/meraki/api-v1/#!create-network-appliance-traffic-shaping-custom-performance-class)

        Args:
            network_id: Network ID.
            name: Name of the custom performance class.
            max_latency: Maximum latency in milliseconds.
            max_jitter: Maximum jitter in milliseconds.
            max_loss_percentage: Maximum percentage of packet loss.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "name": "myCustomPerformanceClass",
              "customPerformanceClassId": "123",
              "maxLatency": 100,
              "maxJitter": 100,
              "maxLossPercentage": 5
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/customPerformanceClasses"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name
        if max_latency is not None:
            payload["maxLatency"] = max_latency
        if max_jitter is not None:
            payload["maxJitter"] = max_jitter
        if max_loss_percentage is not None:
            payload["maxLossPercentage"] = max_loss_percentage

        return self._session.post(
            scope="appliance",
            operation_id="createNetworkApplianceTrafficShapingCustomPerformanceClass",
            path=path,
            json=payload,
            response_schema=CreateNetworkApplianceTrafficShapingCustomPerformanceClassResponse,
        )

    def get_network_appliance_traffic_shaping_custom_performance_class(
        self, *, network_id: str, custom_performance_class_id: str
    ) -> GetNetworkApplianceTrafficShapingCustomPerformanceClassResponse:
        """Return a custom performance class for an MX network.

        [API documentation: getNetworkApplianceTrafficShapingCustomPerformanceClass](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-traffic-shaping-custom-performance-class)

        Args:
            network_id: Network ID.
            custom_performance_class_id: Custom performance class ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "name": "myCustomPerformanceClass",
              "customPerformanceClassId": "123",
              "maxLatency": 100,
              "maxJitter": 100,
              "maxLossPercentage": 5
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        custom_performance_class_id = urllib.parse.quote(str(custom_performance_class_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/customPerformanceClasses/{custom_performance_class_id}"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceTrafficShapingCustomPerformanceClass",
            path=path,
            response_schema=GetNetworkApplianceTrafficShapingCustomPerformanceClassResponse,
        )

    def update_network_appliance_traffic_shaping_custom_performance_class(
        self,
        *,
        network_id: str,
        custom_performance_class_id: str,
        name: str | None = None,
        max_latency: int | None = None,
        max_jitter: int | None = None,
        max_loss_percentage: int | None = None,
    ) -> UpdateNetworkApplianceTrafficShapingCustomPerformanceClassResponse:
        """Update a custom performance class for an MX network.

        [API documentation: updateNetworkApplianceTrafficShapingCustomPerformanceClass](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-traffic-shaping-custom-performance-class)

        Args:
            network_id: Network ID.
            custom_performance_class_id: Custom performance class ID.
            name: Name of the custom performance class.
            max_latency: Maximum latency in milliseconds.
            max_jitter: Maximum jitter in milliseconds.
            max_loss_percentage: Maximum percentage of packet loss.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "name": "myCustomPerformanceClass",
              "customPerformanceClassId": "123",
              "maxLatency": 100,
              "maxJitter": 100,
              "maxLossPercentage": 5
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        custom_performance_class_id = urllib.parse.quote(str(custom_performance_class_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/customPerformanceClasses/{custom_performance_class_id}"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name
        if max_latency is not None:
            payload["maxLatency"] = max_latency
        if max_jitter is not None:
            payload["maxJitter"] = max_jitter
        if max_loss_percentage is not None:
            payload["maxLossPercentage"] = max_loss_percentage

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceTrafficShapingCustomPerformanceClass",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceTrafficShapingCustomPerformanceClassResponse,
        )

    def delete_network_appliance_traffic_shaping_custom_performance_class(
        self, *, network_id: str, custom_performance_class_id: str
    ) -> None:
        """Delete a custom performance class from an MX network.

        [API documentation: deleteNetworkApplianceTrafficShapingCustomPerformanceClass](https://developer.cisco.com/meraki/api-v1/#!delete-network-appliance-traffic-shaping-custom-performance-class)

        Args:
            network_id: Network ID.
            custom_performance_class_id: Custom performance class ID.

        Returns:
            Successful operation.

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        custom_performance_class_id = urllib.parse.quote(str(custom_performance_class_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/customPerformanceClasses/{custom_performance_class_id}"

        return self._session.delete(
            scope="appliance",
            operation_id="deleteNetworkApplianceTrafficShapingCustomPerformanceClass",
            path=path,
        )

    def get_network_appliance_traffic_shaping_rules(self, network_id: str) -> DictResponse:
        """Display the traffic shaping settings rules for an MX network.

        [API documentation: getNetworkApplianceTrafficShapingRules](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-traffic-shaping-rules)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "defaultRulesEnabled": true,
              "rules": [
                {
                  "definitions": [
                    {
                      "type": "host",
                      "value": "google.com"
                    },
                    {
                      "type": "port",
                      "value": "9090"
                    },
                    {
                      "type": "ipRange",
                      "value": "192.1.0.0"
                    },
                    {
                      "type": "ipRange",
                      "value": "192.1.0.0/16"
                    },
                    {
                      "type": "ipRange",
                      "value": "10.1.0.0/16:80"
                    },
                    {
                      "type": "localNet",
                      "value": "192.168.0.0/16"
                    }
                  ],
                  "perClientBandwidthLimits": {
                    "settings": "custom",
                    "bandwidthLimits": {
                      "limitUp": 1000000,
                      "limitDown": 1000000
                    }
                  },
                  "dscpTagValue": 0,
                  "priority": "normal"
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/rules"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceTrafficShapingRules",
            path=path,
            response_schema=DictResponse,
        )

    def update_network_appliance_traffic_shaping_rules(
        self,
        network_id: str,
        *,
        default_rules_enabled: bool | None = None,
        rules: list[UpdateNetworkApplianceTrafficShapingRulesRulesItem] | None = None,
    ) -> DictResponse:
        """Update the traffic shaping settings rules for an MX network.

        [API documentation: updateNetworkApplianceTrafficShapingRules](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-traffic-shaping-rules)

        Args:
            network_id: Network ID.
            default_rules_enabled: Whether default traffic shaping rules are enabled (true) or
                disabled (false). There are 4 default rules, which can be seen on your
                network's traffic shaping page. Note that default rules count against
                the rule limit of 8.
            rules: An array of traffic shaping rules. Rules are applied in the order that they are
                specified in. An empty list (or null) means no rules. Note that you are
                allowed a maximum of 8 rules.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "defaultRulesEnabled": true,
              "rules": [
                {
                  "definitions": [
                    {
                      "type": "host",
                      "value": "google.com"
                    },
                    {
                      "type": "port",
                      "value": "9090"
                    },
                    {
                      "type": "ipRange",
                      "value": "192.1.0.0"
                    },
                    {
                      "type": "ipRange",
                      "value": "192.1.0.0/16"
                    },
                    {
                      "type": "ipRange",
                      "value": "10.1.0.0/16:80"
                    },
                    {
                      "type": "localNet",
                      "value": "192.168.0.0/16"
                    }
                  ],
                  "perClientBandwidthLimits": {
                    "settings": "custom",
                    "bandwidthLimits": {
                      "limitUp": 1000000,
                      "limitDown": 1000000
                    }
                  },
                  "dscpTagValue": 0,
                  "priority": "normal"
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/rules"

        payload: dict[str, Any] = {}
        if default_rules_enabled is not None:
            payload["defaultRulesEnabled"] = default_rules_enabled
        if rules is not None:
            payload["rules"] = [item.model_dump(by_alias=True, exclude_none=True) for item in rules]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceTrafficShapingRules",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def get_network_appliance_traffic_shaping_uplink_bandwidth(
        self, network_id: str
    ) -> GetNetworkApplianceTrafficShapingUplinkBandwidthResponse:
        """Returns the uplink bandwidth limits for your MX network.

        [API documentation: getNetworkApplianceTrafficShapingUplinkBandwidth](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-traffic-shaping-uplink-bandwidth)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "bandwidthLimits": {
                "wan1": {
                  "limitUp": 1000000,
                  "limitDown": 1000000
                },
                "wan2": {
                  "limitUp": 1000000,
                  "limitDown": 1000000
                },
                "cellular": {
                  "limitUp": 51200,
                  "limitDown": 51200
                }
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/uplinkBandwidth"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceTrafficShapingUplinkBandwidth",
            path=path,
            response_schema=GetNetworkApplianceTrafficShapingUplinkBandwidthResponse,
        )

    def update_network_appliance_traffic_shaping_uplink_bandwidth(
        self,
        network_id: str,
        *,
        bandwidth_limits: UpdateNetworkApplianceTrafficShapingUplinkBandwidthBandwidthLimits
        | None = None,
    ) -> DictResponse:
        """Updates the uplink bandwidth settings for your MX network.

        [API documentation: updateNetworkApplianceTrafficShapingUplinkBandwidth](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-traffic-shaping-uplink-bandwidth)

        Args:
            network_id: Network ID.
            bandwidth_limits: A mapping of uplinks to their bandwidth settings (be sure to check
                which uplinks are supported for your network).

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "bandwidthLimits": {
                "wan1": {
                  "limitUp": 1000000,
                  "limitDown": 1000000
                },
                "wan2": {
                  "limitUp": 1000000,
                  "limitDown": 1000000
                },
                "cellular": {
                  "limitUp": 51200,
                  "limitDown": 51200
                }
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/uplinkBandwidth"

        payload: dict[str, Any] = {}
        if bandwidth_limits is not None:
            payload["bandwidthLimits"] = bandwidth_limits.model_dump(
                by_alias=True, exclude_none=True
            )

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceTrafficShapingUplinkBandwidth",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def get_network_appliance_traffic_shaping_uplink_selection(
        self, network_id: str
    ) -> GetNetworkApplianceTrafficShapingUplinkSelectionResponse:
        """Show uplink selection settings for an MX network.

        [API documentation: getNetworkApplianceTrafficShapingUplinkSelection](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-traffic-shaping-uplink-selection)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "activeActiveAutoVpnEnabled": true,
              "defaultUplink": "wan1",
              "loadBalancingEnabled": true,
              "failoverAndFailback": {
                "immediate": {
                  "enabled": true
                }
              },
              "wanTrafficUplinkPreferences": [
                {
                  "trafficFilters": [
                    {
                      "type": "custom",
                      "value": {
                        "protocol": "tcp",
                        "source": {
                          "port": "1-1024",
                          "cidr": "192.168.1.0/24",
                          "vlan": 10,
                          "host": 254
                        },
                        "destination": {
                          "port": "any",
                          "cidr": "any",
                          "applications": [
                            {
                              "id": "meraki:layer7/application/3",
                              "name": "DNS",
                              "type": "major"
                            }
                          ]
                        }
                      }
                    }
                  ],
                  "preferredUplink": "wan1"
                }
              ],
              "vpnTrafficUplinkPreferences": [
                {
                  "trafficFilters": [
                    {
                      "type": "applicationCategory",
                      "value": {
                        "id": "meraki:layer7/category/1",
                        "protocol": "tcp",
                        "source": {
                          "port": "any",
                          "cidr": "192.168.1.0/24",
                          "network": "L_23456789",
                          "vlan": 20,
                          "host": 200
                        },
                        "destination": {
                          "port": "1-1024",
                          "cidr": "any",
                          "network": "L_12345678",
                          "vlan": 10,
                          "host": 254,
                          "fqdn": "www.google.com"
                        }
                      }
                    }
                  ],
                  "preferredUplink": "bestForVoIP",
                  "failOverCriterion": "poorPerformance",
                  "performanceClass": {
                    "type": "custom",
                    "builtinPerformanceClassName": "VoIP",
                    "customPerformanceClassId": "123456"
                  }
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/uplinkSelection"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceTrafficShapingUplinkSelection",
            path=path,
            response_schema=GetNetworkApplianceTrafficShapingUplinkSelectionResponse,
        )

    def update_network_appliance_traffic_shaping_uplink_selection(
        self,
        network_id: str,
        *,
        active_active_auto_vpn_enabled: bool | None = None,
        default_uplink: str | None = None,
        load_balancing_enabled: bool | None = None,
        failover_and_failback: UpdateNetworkApplianceTrafficShapingUplinkSelectionFailoverAndFailback
        | None = None,
        wan_traffic_uplink_preferences: list[
            UpdateNetworkApplianceTrafficShapingUplinkSelectionWanTrafficUplinkPreferencesItem
        ]
        | None = None,
        vpn_traffic_uplink_preferences: list[
            UpdateNetworkApplianceTrafficShapingUplinkSelectionVpnTrafficUplinkPreferencesItem
        ]
        | None = None,
    ) -> UpdateNetworkApplianceTrafficShapingUplinkSelectionResponse:
        """Update uplink selection settings for an MX network.

        [API documentation: updateNetworkApplianceTrafficShapingUplinkSelection](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-traffic-shaping-uplink-selection)

        Args:
            network_id: Network ID.
            active_active_auto_vpn_enabled: Toggle for enabling or disabling active-active AutoVPN.
            default_uplink: The default uplink. Must be a WAN interface 'wanX'.
            load_balancing_enabled: Toggle for enabling or disabling load balancing.
            failover_and_failback: WAN failover and failback behavior.
            wan_traffic_uplink_preferences: Array of uplink preference rules for WAN traffic.
            vpn_traffic_uplink_preferences: Array of uplink preference rules for VPN traffic.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "activeActiveAutoVpnEnabled": true,
              "defaultUplink": "wan1",
              "loadBalancingEnabled": true,
              "failoverAndFailback": {
                "immediate": {
                  "enabled": true
                }
              },
              "wanTrafficUplinkPreferences": [
                {
                  "trafficFilters": [
                    {
                      "type": "custom",
                      "value": {
                        "protocol": "tcp",
                        "source": {
                          "port": "1-1024",
                          "cidr": "192.168.1.0/24",
                          "vlan": 10,
                          "host": 254
                        },
                        "destination": {
                          "port": "any",
                          "cidr": "any",
                          "applications": [
                            {
                              "id": "meraki:layer7/application/3",
                              "name": "DNS",
                              "type": "major"
                            }
                          ]
                        }
                      }
                    }
                  ],
                  "preferredUplink": "wan1"
                }
              ],
              "vpnTrafficUplinkPreferences": [
                {
                  "trafficFilters": [
                    {
                      "type": "applicationCategory",
                      "value": {
                        "id": "meraki:layer7/category/1",
                        "protocol": "tcp",
                        "source": {
                          "port": "any",
                          "cidr": "192.168.1.0/24",
                          "network": "L_23456789",
                          "vlan": 20,
                          "host": 200
                        },
                        "destination": {
                          "port": "1-1024",
                          "cidr": "any",
                          "network": "L_12345678",
                          "vlan": 10,
                          "host": 254,
                          "fqdn": "www.google.com"
                        }
                      }
                    }
                  ],
                  "preferredUplink": "bestForVoIP",
                  "failOverCriterion": "poorPerformance",
                  "performanceClass": {
                    "type": "custom",
                    "builtinPerformanceClassName": "VoIP",
                    "customPerformanceClassId": "123456"
                  }
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/uplinkSelection"

        payload: dict[str, Any] = {}
        if active_active_auto_vpn_enabled is not None:
            payload["activeActiveAutoVpnEnabled"] = active_active_auto_vpn_enabled
        if default_uplink is not None:
            payload["defaultUplink"] = default_uplink
        if load_balancing_enabled is not None:
            payload["loadBalancingEnabled"] = load_balancing_enabled
        if failover_and_failback is not None:
            payload["failoverAndFailback"] = failover_and_failback.model_dump(
                by_alias=True, exclude_none=True
            )
        if wan_traffic_uplink_preferences is not None:
            payload["wanTrafficUplinkPreferences"] = [
                item.model_dump(by_alias=True, exclude_none=True)
                for item in wan_traffic_uplink_preferences
            ]
        if vpn_traffic_uplink_preferences is not None:
            payload["vpnTrafficUplinkPreferences"] = [
                item.model_dump(by_alias=True, exclude_none=True)
                for item in vpn_traffic_uplink_preferences
            ]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceTrafficShapingUplinkSelection",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceTrafficShapingUplinkSelectionResponse,
        )

    def update_network_appliance_traffic_shaping_vpn_exclusions(
        self,
        network_id: str,
        *,
        custom: list[UpdateNetworkApplianceTrafficShapingVpnExclusionsCustomItem] | None = None,
        major_applications: list[
            UpdateNetworkApplianceTrafficShapingVpnExclusionsMajorApplicationsItem
        ]
        | None = None,
    ) -> UpdateNetworkApplianceTrafficShapingVpnExclusionsResponse:
        """Update VPN exclusion rules for an MX network.

        [API documentation: updateNetworkApplianceTrafficShapingVpnExclusions](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-traffic-shaping-vpn-exclusions)

        Args:
            network_id: Network ID.
            custom: Custom VPN exclusion rules. Pass an empty array to clear existing rules.
            major_applications: Major Application based VPN exclusion rules. Pass an empty array to
                clear existing rules.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "networkId": "N_24329156",
              "networkName": "Main Office",
              "custom": [
                {
                  "protocol": "tcp",
                  "destination": "192.168.3.0/24",
                  "port": "8000"
                }
              ],
              "majorApplications": [
                {
                  "id": "meraki:vpnExclusion/application/2",
                  "name": "Office 365 Sharepoint"
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/trafficShaping/vpnExclusions"

        payload: dict[str, Any] = {}
        if custom is not None:
            payload["custom"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in custom
            ]
        if major_applications is not None:
            payload["majorApplications"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in major_applications
            ]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceTrafficShapingVpnExclusions",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceTrafficShapingVpnExclusionsResponse,
        )

    def get_network_appliance_uplinks_usage_history(
        self,
        network_id: str,
        *,
        t0: str | None = None,
        t1: str | None = None,
        timespan: float | None = None,
        resolution: int | None = None,
    ) -> PaginatedResponse[GetNetworkApplianceUplinksUsageHistoryResponseItem]:
        """Get the sent and received bytes for each uplink of a network.

        [API documentation: getNetworkApplianceUplinksUsageHistory](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-uplinks-usage-history)

        Args:
            network_id: Network ID.
            t0: The beginning of the timespan for the data. The maximum lookback period is 365 days
                from today.
            t1: The end of the timespan for the data. t1 can be a maximum of 31 days after t0.
            timespan: The timespan for which the information will be fetched. If specifying
                timespan, do not specify parameters t0 and t1. The value must be in
                seconds and be less than or equal to 31 days. The default is 10 minutes.
            resolution: The time resolution in seconds for returned data. The valid resolutions are:
                60, 300, 600, 1800, 3600, 86400. The default is 60.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "startTime": "2021-07-22T02:00:00Z",
                "endTime": "2021-07-22T03:00:00Z",
                "byInterface": [
                  {
                    "interface": "wan1",
                    "sent": 1562063,
                    "received": 9528787
                  }
                ]
              }
            ]
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/uplinks/usageHistory"

        params: dict[str, Any] = {}
        if t0 is not None:
            params["t0"] = t0
        if t1 is not None:
            params["t1"] = t1
        if timespan is not None:
            params["timespan"] = timespan
        if resolution is not None:
            params["resolution"] = resolution

        return self._session.get_pages(
            scope="appliance",
            operation_id="getNetworkApplianceUplinksUsageHistory",
            path=path,
            params=params,
            item_schema=GetNetworkApplianceUplinksUsageHistoryResponseItem,
        )

    def get_network_appliance_vlans(
        self, network_id: str
    ) -> PaginatedResponse[GetNetworkApplianceVlansResponseItem]:
        """List the VLANs for a Security Appliance network.

        [API documentation: getNetworkApplianceVlans](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-vlans)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "id": "1234",
                "interfaceId": "1284392014819",
                "name": "My VLAN",
                "subnet": "192.168.1.0/24",
                "applianceIp": "192.168.1.2",
                "groupPolicyId": "101",
                "templateVlanType": "same",
                "cidr": "192.168.1.0/24",
                "mask": 28,
                "dhcpRelayServerIps": [
                  "192.168.1.0",
                  "192.168.128.0"
                ],
                "dhcpHandling": "Run a DHCP server",
                "dhcpLeaseTime": "1 day",
                "dhcpBootOptionsEnabled": false,
                "dhcpBootNextServer": "1.2.3.4",
                "dhcpBootFilename": "sample.file",
                "fixedIpAssignments": {
                  "00:11:22:33:44:55": {
                    "ip": "1.2.3.4",
                    "name": "My favorite IP"
                  }
                },
                "reservedIpRanges": [
                  {
                    "start": "192.168.1.0",
                    "end": "192.168.1.1",
                    "comment": "A reserved IP range"
                  }
                ],
                "dnsNameservers": "google_dns",
                "dhcpOptions": [
                  {
                    "code": "5",
                    "type": "text",
                    "value": "five"
                  }
                ],
                "vpnNatSubnet": "192.168.1.0/24",
                "mandatoryDhcp": {
                  "enabled": true
                },
                "ipv6": {
                  "enabled": true,
                  "prefixAssignments": [
                    {
                      "autonomous": false,
                      "staticPrefix": "2001:db8:3c4d:15::/64",
                      "staticApplianceIp6": "2001:db8:3c4d:15::1",
                      "origin": {
                        "type": "internet",
                        "interfaces": [
                          "wan0"
                        ]
                      }
                    }
                  ]
                }
              }
            ]
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/vlans"

        return self._session.get_pages(
            scope="appliance",
            operation_id="getNetworkApplianceVlans",
            path=path,
            item_schema=GetNetworkApplianceVlansResponseItem,
        )

    def create_network_appliance_vlan(
        self,
        *,
        network_id: str,
        id: str,
        name: str,
        subnet: str | None = None,
        appliance_ip: str | None = None,
        group_policy_id: str | None = None,
        template_vlan_type: CreateNetworkApplianceVlanTemplateVlanType | None = None,
        cidr: str | None = None,
        mask: int | None = None,
        ipv6: CreateNetworkApplianceVlanIpv6 | None = None,
        dhcp_handling: CreateNetworkApplianceVlanDhcpHandling | None = None,
        dhcp_relay_server_ips: list[str] | None = None,
        dhcp_lease_time: CreateNetworkApplianceVlanDhcpLeaseTime | None = None,
        mandatory_dhcp: CreateNetworkApplianceVlanMandatoryDhcp | None = None,
        dhcp_boot_options_enabled: bool | None = None,
        dhcp_boot_next_server: str | None = None,
        dhcp_boot_filename: str | None = None,
        dhcp_options: list[CreateNetworkApplianceVlanDhcpOptionsItem] | None = None,
    ) -> CreateNetworkApplianceVlanResponse:
        """Add a VLAN.

        [API documentation: createNetworkApplianceVlan](https://developer.cisco.com/meraki/api-v1/#!create-network-appliance-vlan)

        Args:
            network_id: Network ID.
            id: The VLAN ID of the new VLAN (must be between 1 and 4094).
            name: The name of the new VLAN.
            subnet: The subnet of the VLAN.
            appliance_ip: The local IP of the appliance on the VLAN.
            group_policy_id: The id of the desired group policy to apply to the VLAN.
            template_vlan_type: Type of subnetting of the VLAN. Applicable only for template
                network.
            cidr: CIDR of the pool of subnets. Applicable only for template network. Each network
                bound to the template will automatically pick a subnet from this pool to
                build its own VLAN.
            mask: Mask used for the subnet of all bound to the template networks. Applicable only
                for template network.
            ipv6: IPv6 configuration on the VLAN.
            dhcp_handling: The appliance's handling of DHCP requests on this VLAN. One of: 'Run a
                DHCP server', 'Relay DHCP to another server' or 'Do not respond to DHCP
                requests'.
            dhcp_relay_server_ips: The IPs (IPv4) of the DHCP servers that DHCP requests should be
                relayed to. CIDR/subnet notation and hostnames are not supported.
            dhcp_lease_time: The term of DHCP leases if the appliance is running a DHCP server on
                this VLAN. One of: '30 minutes', '1 hour', '4 hours', '12 hours', '1
                day' or '1 week'.
            mandatory_dhcp: Mandatory DHCP will enforce that clients connecting to this VLAN must
                use the IP address assigned by the DHCP server. Clients who use a static
                IP address won't be able to associate. Only available on firmware
                versions 17.0 and above.
            dhcp_boot_options_enabled: Use DHCP boot options specified in other properties.
            dhcp_boot_next_server: DHCP boot option to direct boot clients to the server to load the
                boot file from.
            dhcp_boot_filename: DHCP boot option for boot filename.
            dhcp_options: The list of DHCP options that will be included in DHCP responses. Each
                object in the list should have "code", "type", and "value" properties.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "id": "1234",
              "interfaceId": "1284392014819",
              "name": "My VLAN",
              "subnet": "192.168.1.0/24",
              "applianceIp": "192.168.1.2",
              "groupPolicyId": "101",
              "templateVlanType": "same",
              "cidr": "192.168.1.0/24",
              "mask": 28,
              "mandatoryDhcp": {
                "enabled": true
              },
              "ipv6": {
                "enabled": true,
                "prefixAssignments": [
                  {
                    "autonomous": false,
                    "staticPrefix": "2001:db8:3c4d:15::/64",
                    "staticApplianceIp6": "2001:db8:3c4d:15::1",
                    "origin": {
                      "type": "internet",
                      "interfaces": [
                        "wan0"
                      ]
                    }
                  }
                ]
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/vlans"

        payload: dict[str, Any] = {}
        if id is not None:
            payload["id"] = id
        if name is not None:
            payload["name"] = name
        if subnet is not None:
            payload["subnet"] = subnet
        if appliance_ip is not None:
            payload["applianceIp"] = appliance_ip
        if group_policy_id is not None:
            payload["groupPolicyId"] = group_policy_id
        if template_vlan_type is not None:
            payload["templateVlanType"] = template_vlan_type
        if cidr is not None:
            payload["cidr"] = cidr
        if mask is not None:
            payload["mask"] = mask
        if ipv6 is not None:
            payload["ipv6"] = ipv6.model_dump(by_alias=True, exclude_none=True)
        if dhcp_handling is not None:
            payload["dhcpHandling"] = dhcp_handling
        if dhcp_relay_server_ips is not None:
            payload["dhcpRelayServerIps"] = dhcp_relay_server_ips
        if dhcp_lease_time is not None:
            payload["dhcpLeaseTime"] = dhcp_lease_time
        if mandatory_dhcp is not None:
            payload["mandatoryDhcp"] = mandatory_dhcp.model_dump(by_alias=True, exclude_none=True)
        if dhcp_boot_options_enabled is not None:
            payload["dhcpBootOptionsEnabled"] = dhcp_boot_options_enabled
        if dhcp_boot_next_server is not None:
            payload["dhcpBootNextServer"] = dhcp_boot_next_server
        if dhcp_boot_filename is not None:
            payload["dhcpBootFilename"] = dhcp_boot_filename
        if dhcp_options is not None:
            payload["dhcpOptions"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in dhcp_options
            ]

        return self._session.post(
            scope="appliance",
            operation_id="createNetworkApplianceVlan",
            path=path,
            json=payload,
            response_schema=CreateNetworkApplianceVlanResponse,
        )

    def get_network_appliance_vlans_settings(
        self, network_id: str
    ) -> GetNetworkApplianceVlansSettingsResponse:
        """Returns the enabled status of VLANs for the network.

        [API documentation: getNetworkApplianceVlansSettings](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-vlans-settings)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "vlansEnabled": true
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/vlans/settings"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceVlansSettings",
            path=path,
            response_schema=GetNetworkApplianceVlansSettingsResponse,
        )

    def update_network_appliance_vlans_settings(
        self, network_id: str, *, vlans_enabled: bool | None = None
    ) -> UpdateNetworkApplianceVlansSettingsResponse:
        """Enable/Disable VLANs for the given network.

        [API documentation: updateNetworkApplianceVlansSettings](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-vlans-settings)

        Args:
            network_id: Network ID.
            vlans_enabled: Boolean indicating whether to enable (true) or disable (false) VLANs for
                the network.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "vlansEnabled": true
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/vlans/settings"

        payload: dict[str, Any] = {}
        if vlans_enabled is not None:
            payload["vlansEnabled"] = vlans_enabled

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceVlansSettings",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceVlansSettingsResponse,
        )

    def get_network_appliance_vlan(
        self, *, network_id: str, vlan_id: str
    ) -> GetNetworkApplianceVlanResponse:
        """Return a VLAN.

        [API documentation: getNetworkApplianceVlan](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-vlan)

        Args:
            network_id: Network ID.
            vlan_id: Vlan ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "id": "1234",
              "interfaceId": "1284392014819",
              "name": "My VLAN",
              "subnet": "192.168.1.0/24",
              "applianceIp": "192.168.1.2",
              "groupPolicyId": "101",
              "templateVlanType": "same",
              "cidr": "192.168.1.0/24",
              "mask": 28,
              "dhcpRelayServerIps": [
                "192.168.1.0",
                "192.168.128.0"
              ],
              "dhcpHandling": "Run a DHCP server",
              "dhcpLeaseTime": "1 day",
              "dhcpBootOptionsEnabled": false,
              "dhcpBootNextServer": "1.2.3.4",
              "dhcpBootFilename": "sample.file",
              "fixedIpAssignments": {
                "00:11:22:33:44:55": {
                  "ip": "1.2.3.4",
                  "name": "My favorite IP"
                }
              },
              "reservedIpRanges": [
                {
                  "start": "192.168.1.0",
                  "end": "192.168.1.1",
                  "comment": "A reserved IP range"
                }
              ],
              "dnsNameservers": "google_dns",
              "dhcpOptions": [
                {
                  "code": "5",
                  "type": "text",
                  "value": "five"
                }
              ],
              "vpnNatSubnet": "192.168.1.0/24",
              "mandatoryDhcp": {
                "enabled": true
              },
              "ipv6": {
                "enabled": true,
                "prefixAssignments": [
                  {
                    "autonomous": false,
                    "staticPrefix": "2001:db8:3c4d:15::/64",
                    "staticApplianceIp6": "2001:db8:3c4d:15::1",
                    "origin": {
                      "type": "internet",
                      "interfaces": [
                        "wan0"
                      ]
                    }
                  }
                ]
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        vlan_id = urllib.parse.quote(str(vlan_id), safe="")
        path = f"/networks/{network_id}/appliance/vlans/{vlan_id}"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceVlan",
            path=path,
            response_schema=GetNetworkApplianceVlanResponse,
        )

    def update_network_appliance_vlan(
        self,
        *,
        network_id: str,
        vlan_id: str,
        name: str | None = None,
        subnet: str | None = None,
        appliance_ip: str | None = None,
        group_policy_id: str | None = None,
        vpn_nat_subnet: str | None = None,
        dhcp_handling: UpdateNetworkApplianceVlanDhcpHandling | None = None,
        dhcp_relay_server_ips: list[str] | None = None,
        dhcp_lease_time: UpdateNetworkApplianceVlanDhcpLeaseTime | None = None,
        dhcp_boot_options_enabled: bool | None = None,
        dhcp_boot_next_server: str | None = None,
        dhcp_boot_filename: str | None = None,
        fixed_ip_assignments: dict[str, Any] | None = None,
        reserved_ip_ranges: list[UpdateNetworkApplianceVlanReservedIpRangesItem] | None = None,
        dns_nameservers: str | None = None,
        dhcp_options: list[UpdateNetworkApplianceVlanDhcpOptionsItem] | None = None,
        template_vlan_type: UpdateNetworkApplianceVlanTemplateVlanType | None = None,
        cidr: str | None = None,
        mask: int | None = None,
        ipv6: UpdateNetworkApplianceVlanIpv6 | None = None,
        mandatory_dhcp: UpdateNetworkApplianceVlanMandatoryDhcp | None = None,
    ) -> UpdateNetworkApplianceVlanResponse:
        """Update a VLAN.

        [API documentation: updateNetworkApplianceVlan](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-vlan)

        Args:
            network_id: Network ID.
            vlan_id: Vlan ID.
            name: The name of the VLAN.
            subnet: The subnet of the VLAN.
            appliance_ip: The local IP of the appliance on the VLAN.
            group_policy_id: The id of the desired group policy to apply to the VLAN.
            vpn_nat_subnet: The translated VPN subnet if VPN and VPN subnet translation are enabled
                on the VLAN.
            dhcp_handling: The appliance's handling of DHCP requests on this VLAN. One of: 'Run a
                DHCP server', 'Relay DHCP to another server' or 'Do not respond to DHCP
                requests'.
            dhcp_relay_server_ips: The IPs (IPv4) of the DHCP servers that DHCP requests should be
                relayed to. CIDR/subnet notation and hostnames are not supported.
            dhcp_lease_time: The term of DHCP leases if the appliance is running a DHCP server on
                this VLAN. One of: '30 minutes', '1 hour', '4 hours', '12 hours', '1
                day' or '1 week'.
            dhcp_boot_options_enabled: Use DHCP boot options specified in other properties.
            dhcp_boot_next_server: DHCP boot option to direct boot clients to the server to load the
                boot file from.
            dhcp_boot_filename: DHCP boot option for boot filename.
            fixed_ip_assignments: The DHCP fixed IP assignments on the VLAN. This should be an
                object that contains mappings from MAC addresses to objects that
                themselves each contain "ip" and "name" string fields. See the sample
                request/response for more details.
            reserved_ip_ranges: The DHCP reserved IP ranges on the VLAN.
            dns_nameservers: The DNS nameservers used for DHCP responses, either "upstream_dns",
                "google_dns", "opendns", or a newline seperated string of IP addresses
                or domain names.
            dhcp_options: The list of DHCP options that will be included in DHCP responses. Each
                object in the list should have "code", "type", and "value" properties.
            template_vlan_type: Type of subnetting of the VLAN. Applicable only for template
                network.
            cidr: CIDR of the pool of subnets. Applicable only for template network. Each network
                bound to the template will automatically pick a subnet from this pool to
                build its own VLAN.
            mask: Mask used for the subnet of all bound to the template networks. Applicable only
                for template network.
            ipv6: IPv6 configuration on the VLAN.
            mandatory_dhcp: Mandatory DHCP will enforce that clients connecting to this VLAN must
                use the IP address assigned by the DHCP server. Clients who use a static
                IP address won't be able to associate. Only available on firmware
                versions 17.0 and above.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "id": "1234",
              "interfaceId": "1284392014819",
              "name": "My VLAN",
              "subnet": "192.168.1.0/24",
              "applianceIp": "192.168.1.2",
              "groupPolicyId": "101",
              "templateVlanType": "same",
              "cidr": "192.168.1.0/24",
              "mask": 28,
              "dhcpRelayServerIps": [
                "192.168.1.0",
                "192.168.128.0"
              ],
              "dhcpHandling": "Run a DHCP server",
              "dhcpLeaseTime": "1 day",
              "dhcpBootOptionsEnabled": false,
              "dhcpBootNextServer": "1.2.3.4",
              "dhcpBootFilename": "sample.file",
              "fixedIpAssignments": {
                "00:11:22:33:44:55": {
                  "ip": "1.2.3.4",
                  "name": "My favorite IP"
                }
              },
              "reservedIpRanges": [
                {
                  "start": "192.168.1.0",
                  "end": "192.168.1.1",
                  "comment": "A reserved IP range"
                }
              ],
              "dnsNameservers": "google_dns",
              "dhcpOptions": [
                {
                  "code": "5",
                  "type": "text",
                  "value": "five"
                }
              ],
              "vpnNatSubnet": "192.168.1.0/24",
              "mandatoryDhcp": {
                "enabled": true
              },
              "ipv6": {
                "enabled": true,
                "prefixAssignments": [
                  {
                    "autonomous": false,
                    "staticPrefix": "2001:db8:3c4d:15::/64",
                    "staticApplianceIp6": "2001:db8:3c4d:15::1",
                    "origin": {
                      "type": "internet",
                      "interfaces": [
                        "wan0"
                      ]
                    }
                  }
                ]
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        vlan_id = urllib.parse.quote(str(vlan_id), safe="")
        path = f"/networks/{network_id}/appliance/vlans/{vlan_id}"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name
        if subnet is not None:
            payload["subnet"] = subnet
        if appliance_ip is not None:
            payload["applianceIp"] = appliance_ip
        if group_policy_id is not None:
            payload["groupPolicyId"] = group_policy_id
        if vpn_nat_subnet is not None:
            payload["vpnNatSubnet"] = vpn_nat_subnet
        if dhcp_handling is not None:
            payload["dhcpHandling"] = dhcp_handling
        if dhcp_relay_server_ips is not None:
            payload["dhcpRelayServerIps"] = dhcp_relay_server_ips
        if dhcp_lease_time is not None:
            payload["dhcpLeaseTime"] = dhcp_lease_time
        if dhcp_boot_options_enabled is not None:
            payload["dhcpBootOptionsEnabled"] = dhcp_boot_options_enabled
        if dhcp_boot_next_server is not None:
            payload["dhcpBootNextServer"] = dhcp_boot_next_server
        if dhcp_boot_filename is not None:
            payload["dhcpBootFilename"] = dhcp_boot_filename
        if fixed_ip_assignments is not None:
            payload["fixedIpAssignments"] = fixed_ip_assignments
        if reserved_ip_ranges is not None:
            payload["reservedIpRanges"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in reserved_ip_ranges
            ]
        if dns_nameservers is not None:
            payload["dnsNameservers"] = dns_nameservers
        if dhcp_options is not None:
            payload["dhcpOptions"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in dhcp_options
            ]
        if template_vlan_type is not None:
            payload["templateVlanType"] = template_vlan_type
        if cidr is not None:
            payload["cidr"] = cidr
        if mask is not None:
            payload["mask"] = mask
        if ipv6 is not None:
            payload["ipv6"] = ipv6.model_dump(by_alias=True, exclude_none=True)
        if mandatory_dhcp is not None:
            payload["mandatoryDhcp"] = mandatory_dhcp.model_dump(by_alias=True, exclude_none=True)

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceVlan",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceVlanResponse,
        )

    def delete_network_appliance_vlan(self, *, network_id: str, vlan_id: str) -> None:
        """Delete a VLAN from a network.

        [API documentation: deleteNetworkApplianceVlan](https://developer.cisco.com/meraki/api-v1/#!delete-network-appliance-vlan)

        Args:
            network_id: Network ID.
            vlan_id: Vlan ID.

        Returns:
            Successful operation.

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        vlan_id = urllib.parse.quote(str(vlan_id), safe="")
        path = f"/networks/{network_id}/appliance/vlans/{vlan_id}"

        return self._session.delete(
            scope="appliance", operation_id="deleteNetworkApplianceVlan", path=path
        )

    def get_network_appliance_vpn_bgp(self, network_id: str) -> GetNetworkApplianceVpnBgpResponse:
        """Return a Hub BGP Configuration.

        [API documentation: getNetworkApplianceVpnBgp](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-vpn-bgp)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "enabled": true,
              "asNumber": 64515,
              "ibgpHoldTimer": 120,
              "neighbors": [
                {
                  "ip": "10.10.10.22",
                  "ipv6": {
                    "address": "2002::1234:abcd:ffff:c0a8:101"
                  },
                  "remoteAsNumber": 64343,
                  "receiveLimit": 120,
                  "allowTransit": true,
                  "ebgpHoldTimer": 180,
                  "ebgpMultihop": 2,
                  "sourceInterface": "wan1",
                  "nextHopIp": "1.2.3.4",
                  "ttlSecurity": {
                    "enabled": false
                  },
                  "authentication": {
                    "password": "abc123"
                  },
                  "multiExitDiscriminator": 2,
                  "pathPrepend": [
                    1,
                    2
                  ],
                  "weight": 10
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/vpn/bgp"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceVpnBgp",
            path=path,
            response_schema=GetNetworkApplianceVpnBgpResponse,
        )

    def update_network_appliance_vpn_bgp(
        self,
        *,
        network_id: str,
        enabled: bool,
        as_number: int | None = None,
        ibgp_hold_timer: int | None = None,
        neighbors: list[UpdateNetworkApplianceVpnBgpNeighborsItem] | None = None,
    ) -> UpdateNetworkApplianceVpnBgpResponse:
        """Update a Hub BGP Configuration.

        [API documentation: updateNetworkApplianceVpnBgp](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-vpn-bgp)

        Args:
            network_id: Network ID.
            enabled: Boolean value to enable or disable the BGP configuration. When BGP is enabled,
                the asNumber (ASN) will be autopopulated with the preconfigured ASN at
                other Hubs or a default value if there is no ASN configured.
            as_number: An Autonomous System Number (ASN) is required if you are to run BGP and peer
                with another BGP Speaker outside of the Auto VPN domain. This ASN will
                be applied to the entire Auto VPN domain. The entire 4-byte ASN range is
                supported. So, the ASN must be an integer between 1 and 4294967295. When
                absent, this field is not updated. If no value exists then it defaults
                to 64512.
            ibgp_hold_timer: The iBGP holdtimer in seconds. The iBGP holdtimer must be an integer
                between 12 and 240. When absent, this field is not updated. If no value
                exists then it defaults to 240.
            neighbors: List of BGP neighbors. This list replaces the existing set of neighbors. When
                absent, this field is not updated.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "enabled": true,
              "asNumber": 64515,
              "ibgpHoldTimer": 120,
              "neighbors": [
                {
                  "ip": "10.10.10.22",
                  "ipv6": {
                    "address": "2002::1234:abcd:ffff:c0a8:101"
                  },
                  "remoteAsNumber": 64343,
                  "receiveLimit": 120,
                  "allowTransit": true,
                  "ebgpHoldTimer": 180,
                  "ebgpMultihop": 2,
                  "sourceInterface": "wan1",
                  "nextHopIp": "1.2.3.4",
                  "ttlSecurity": {
                    "enabled": false
                  },
                  "authentication": {
                    "password": "abc123"
                  },
                  "multiExitDiscriminator": 2,
                  "pathPrepend": [
                    1,
                    2
                  ],
                  "weight": 10
                }
              ]
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/vpn/bgp"

        payload: dict[str, Any] = {}
        if enabled is not None:
            payload["enabled"] = enabled
        if as_number is not None:
            payload["asNumber"] = as_number
        if ibgp_hold_timer is not None:
            payload["ibgpHoldTimer"] = ibgp_hold_timer
        if neighbors is not None:
            payload["neighbors"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in neighbors
            ]

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceVpnBgp",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceVpnBgpResponse,
        )

    def get_network_appliance_vpn_site_to_site_vpn(
        self, network_id: str
    ) -> GetNetworkApplianceVpnSiteToSiteVpnResponse:
        """Return the site-to-site VPN settings of a network.

        [API documentation: getNetworkApplianceVpnSiteToSiteVpn](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-vpn-site-to-site-vpn)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "mode": "spoke",
              "hubs": [
                {
                  "hubId": "N_4901849",
                  "useDefaultRoute": true
                }
              ],
              "subnets": [
                {
                  "localSubnet": "192.168.1.0/24",
                  "useVpn": true,
                  "nat": {
                    "enabled": true,
                    "remoteSubnet": "192.168.2.0/24"
                  }
                }
              ],
              "subnet": {
                "nat": {
                  "isAllowed": true
                }
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/vpn/siteToSiteVpn"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceVpnSiteToSiteVpn",
            path=path,
            response_schema=GetNetworkApplianceVpnSiteToSiteVpnResponse,
        )

    def update_network_appliance_vpn_site_to_site_vpn(
        self,
        *,
        network_id: str,
        mode: UpdateNetworkApplianceVpnSiteToSiteVpnMode,
        hubs: list[UpdateNetworkApplianceVpnSiteToSiteVpnHubsItem] | None = None,
        subnets: list[UpdateNetworkApplianceVpnSiteToSiteVpnSubnetsItem] | None = None,
        subnet: UpdateNetworkApplianceVpnSiteToSiteVpnSubnet | None = None,
    ) -> UpdateNetworkApplianceVpnSiteToSiteVpnResponse:
        """Update the site-to-site VPN settings of a network.

        [API documentation: updateNetworkApplianceVpnSiteToSiteVpn](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-vpn-site-to-site-vpn)

        Args:
            network_id: Network ID.
            mode: The site-to-site VPN mode. Can be one of 'none', 'spoke' or 'hub'.
            hubs: The list of VPN hubs, in order of preference. In spoke mode, at least 1 hub is
                required.
            subnets: The list of subnets and their VPN presence.
            subnet: Configuration of subnet features.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "mode": "spoke",
              "hubs": [
                {
                  "hubId": "N_4901849",
                  "useDefaultRoute": true
                }
              ],
              "subnets": [
                {
                  "localSubnet": "192.168.1.0/24",
                  "useVpn": true,
                  "nat": {
                    "enabled": true,
                    "remoteSubnet": "192.168.2.0/24"
                  }
                }
              ],
              "subnet": {
                "nat": {
                  "isAllowed": true
                }
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/vpn/siteToSiteVpn"

        payload: dict[str, Any] = {}
        if mode is not None:
            payload["mode"] = mode
        if hubs is not None:
            payload["hubs"] = [item.model_dump(by_alias=True, exclude_none=True) for item in hubs]
        if subnets is not None:
            payload["subnets"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in subnets
            ]
        if subnet is not None:
            payload["subnet"] = subnet.model_dump(by_alias=True, exclude_none=True)

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceVpnSiteToSiteVpn",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceVpnSiteToSiteVpnResponse,
        )

    def get_network_appliance_warm_spare(
        self, network_id: str
    ) -> GetNetworkApplianceWarmSpareResponse:
        """Return MX warm spare settings.

        [API documentation: getNetworkApplianceWarmSpare](https://developer.cisco.com/meraki/api-v1/#!get-network-appliance-warm-spare)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "enabled": true,
              "primarySerial": "Q234-ABCD-5678",
              "spareSerial": "Q234-ABCD-5678",
              "uplinkMode": "virtual",
              "wan1": {
                "ip": "1.2.3.4",
                "subnet": "192.168.1.0/24"
              },
              "wan2": {
                "ip": "1.2.3.4",
                "subnet": "192.168.1.0/24"
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/warmSpare"

        return self._session.get(
            scope="appliance",
            operation_id="getNetworkApplianceWarmSpare",
            path=path,
            response_schema=GetNetworkApplianceWarmSpareResponse,
        )

    def update_network_appliance_warm_spare(
        self,
        *,
        network_id: str,
        enabled: bool,
        spare_serial: str | None = None,
        uplink_mode: str | None = None,
        virtual_ip1: str | None = None,
        virtual_ip2: str | None = None,
    ) -> UpdateNetworkApplianceWarmSpareResponse:
        """Update MX warm spare settings.

        [API documentation: updateNetworkApplianceWarmSpare](https://developer.cisco.com/meraki/api-v1/#!update-network-appliance-warm-spare)

        Args:
            network_id: Network ID.
            enabled: Enable warm spare.
            spare_serial: Serial number of the warm spare appliance.
            uplink_mode: Uplink mode, either virtual or public.
            virtual_ip1: The WAN 1 shared IP.
            virtual_ip2: The WAN 2 shared IP.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "enabled": true,
              "primarySerial": "Q234-ABCD-5678",
              "spareSerial": "Q234-ABCD-5678",
              "uplinkMode": "virtual",
              "wan1": {
                "ip": "1.2.3.4",
                "subnet": "192.168.1.0/24"
              },
              "wan2": {
                "ip": "1.2.3.4",
                "subnet": "192.168.1.0/24"
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/warmSpare"

        payload: dict[str, Any] = {}
        if enabled is not None:
            payload["enabled"] = enabled
        if spare_serial is not None:
            payload["spareSerial"] = spare_serial
        if uplink_mode is not None:
            payload["uplinkMode"] = uplink_mode
        if virtual_ip1 is not None:
            payload["virtualIp1"] = virtual_ip1
        if virtual_ip2 is not None:
            payload["virtualIp2"] = virtual_ip2

        return self._session.put(
            scope="appliance",
            operation_id="updateNetworkApplianceWarmSpare",
            path=path,
            json=payload,
            response_schema=UpdateNetworkApplianceWarmSpareResponse,
        )

    def swap_network_appliance_warm_spare(
        self, network_id: str
    ) -> SwapNetworkApplianceWarmSpareResponse:
        """Swap MX primary and warm spare appliances.

        [API documentation: swapNetworkApplianceWarmSpare](https://developer.cisco.com/meraki/api-v1/#!swap-network-appliance-warm-spare)

        Args:
            network_id: Network ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "enabled": true,
              "primarySerial": "Q234-ABCD-5678",
              "spareSerial": "Q234-ABCD-5678",
              "uplinkMode": "virtual",
              "wan1": {
                "ip": "1.2.3.4",
                "subnet": "192.168.1.0/24"
              },
              "wan2": {
                "ip": "1.2.3.4",
                "subnet": "192.168.1.0/24"
              }
            }
            ```

        """
        network_id = urllib.parse.quote(str(network_id), safe="")
        path = f"/networks/{network_id}/appliance/warmSpare/swap"

        return self._session.post(
            scope="appliance",
            operation_id="swapNetworkApplianceWarmSpare",
            path=path,
            response_schema=SwapNetworkApplianceWarmSpareResponse,
        )

    def get_organization_appliance_dns_local_profiles(
        self, organization_id: str, *, profile_ids: list[str] | None = None
    ) -> PaginatedResponse[GetOrganizationApplianceDnsLocalProfilesResponseItem]:
        """Fetch the local DNS profiles used in the organization.

        [API documentation: getOrganizationApplianceDnsLocalProfiles](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-dns-local-profiles)

        Args:
            organization_id: Organization ID.
            profile_ids: Optional parameter to filter the results by profile IDs.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "profileId": "123456",
                "name": "Default profile"
              }
            ]
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/local/profiles"

        params: dict[str, Any] = {}
        if profile_ids is not None:
            params["profileIds[]"] = profile_ids

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceDnsLocalProfiles",
            path=path,
            params=params,
            item_schema=GetOrganizationApplianceDnsLocalProfilesResponseItem,
        )

    def create_organization_appliance_dns_local_profile(
        self, *, organization_id: str, name: str
    ) -> CreateOrganizationApplianceDnsLocalProfileResponse:
        """Create a new local DNS profile.

        [API documentation: createOrganizationApplianceDnsLocalProfile](https://developer.cisco.com/meraki/api-v1/#!create-organization-appliance-dns-local-profile)

        Args:
            organization_id: Organization ID.
            name: Name of profile.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "profileId": "123456",
              "name": "Default profile"
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/local/profiles"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name

        return self._session.post(
            scope="appliance",
            operation_id="createOrganizationApplianceDnsLocalProfile",
            path=path,
            json=payload,
            response_schema=CreateOrganizationApplianceDnsLocalProfileResponse,
        )

    def get_organization_appliance_dns_local_profiles_assignments(
        self,
        organization_id: str,
        *,
        profile_ids: list[str] | None = None,
        network_ids: list[str] | None = None,
    ) -> PaginatedResponse[GetOrganizationApplianceDnsLocalProfilesAssignmentsResponseItemsItem]:
        """Fetch the local DNS profile assignments in the organization.

        [API documentation: getOrganizationApplianceDnsLocalProfilesAssignments](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-dns-local-profiles-assignments)

        Args:
            organization_id: Organization ID.
            profile_ids: Optional parameter to filter the results by profile IDs.
            network_ids: Optional parameter to filter the results by network IDs.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            {
              "items": [
                {
                  "assignmentId": "123456",
                  "network": {
                    "id": "N_123456"
                  },
                  "profile": {
                    "id": "1234"
                  }
                }
              ],
              "meta": {
                "counts": {
                  "items": {
                    "total": 10,
                    "remaining": 0
                  }
                }
              }
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/local/profiles/assignments"

        params: dict[str, Any] = {}
        if profile_ids is not None:
            params["profileIds[]"] = profile_ids
        if network_ids is not None:
            params["networkIds[]"] = network_ids

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceDnsLocalProfilesAssignments",
            path=path,
            params=params,
            item_schema=GetOrganizationApplianceDnsLocalProfilesAssignmentsResponseItemsItem,
        )

    def bulk_organization_appliance_dns_local_profiles_assignments_create(
        self,
        *,
        organization_id: str,
        items: list[BulkOrganizationApplianceDnsLocalProfilesAssignmentsCreateItemsItem],
    ) -> BulkOrganizationApplianceDnsLocalProfilesAssignmentsCreateResponse:
        """Assign the local DNS profile to networks in the organization.

        [API documentation: bulkOrganizationApplianceDnsLocalProfilesAssignmentsCreate](https://developer.cisco.com/meraki/api-v1/#!bulk-organization-appliance-dns-local-profiles-assignments-create)

        Args:
            organization_id: Organization ID.
            items: List containing the network ID and Profile ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "items": [
                {
                  "assignmentId": "123456",
                  "network": {
                    "id": "N_123456"
                  },
                  "profile": {
                    "id": "1234"
                  }
                }
              ]
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = (
            f"/organizations/{organization_id}/appliance/dns/local/profiles/assignments/bulkCreate"
        )

        payload: dict[str, Any] = {}
        if items is not None:
            payload["items"] = [item.model_dump(by_alias=True, exclude_none=True) for item in items]

        return self._session.post(
            scope="appliance",
            operation_id="bulkOrganizationApplianceDnsLocalProfilesAssignmentsCreate",
            path=path,
            json=payload,
            response_schema=BulkOrganizationApplianceDnsLocalProfilesAssignmentsCreateResponse,
        )

    def create_organization_appliance_dns_local_profiles_assignments_bulk_delete(
        self,
        *,
        organization_id: str,
        items: list[CreateOrganizationApplianceDnsLocalProfilesAssignmentsBulkDeleteItemsItem],
    ) -> CreateOrganizationApplianceDnsLocalProfilesAssignmentsBulkDeleteResponse:
        """Unassign the local DNS profile to networks in the organization.

        [API documentation: createOrganizationApplianceDnsLocalProfilesAssignmentsBulkDelete](https://developer.cisco.com/meraki/api-v1/#!create-organization-appliance-dns-local-profiles-assignments-bulk-delete)

        Args:
            organization_id: Organization ID.
            items: List containing the assignment ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "items": [
                {
                  "assignmentId": "123456",
                  "network": {
                    "id": "N_123456"
                  },
                  "profile": {
                    "id": "1234"
                  }
                }
              ]
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = (
            f"/organizations/{organization_id}/appliance/dns/local/profiles/assignments/bulkDelete"
        )

        payload: dict[str, Any] = {}
        if items is not None:
            payload["items"] = [item.model_dump(by_alias=True, exclude_none=True) for item in items]

        return self._session.post(
            scope="appliance",
            operation_id="createOrganizationApplianceDnsLocalProfilesAssignmentsBulkDelete",
            path=path,
            json=payload,
            response_schema=CreateOrganizationApplianceDnsLocalProfilesAssignmentsBulkDeleteResponse,
        )

    def update_organization_appliance_dns_local_profile(
        self, *, organization_id: str, profile_id: str, name: str
    ) -> UpdateOrganizationApplianceDnsLocalProfileResponse:
        """Update a local DNS profile.

        [API documentation: updateOrganizationApplianceDnsLocalProfile](https://developer.cisco.com/meraki/api-v1/#!update-organization-appliance-dns-local-profile)

        Args:
            organization_id: Organization ID.
            profile_id: Profile ID.
            name: Name of profile.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "profileId": "123456",
              "name": "Default profile"
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        profile_id = urllib.parse.quote(str(profile_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/local/profiles/{profile_id}"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name

        return self._session.put(
            scope="appliance",
            operation_id="updateOrganizationApplianceDnsLocalProfile",
            path=path,
            json=payload,
            response_schema=UpdateOrganizationApplianceDnsLocalProfileResponse,
        )

    def delete_organization_appliance_dns_local_profile(
        self, *, organization_id: str, profile_id: str
    ) -> None:
        """Deletes a local DNS profile.

        [API documentation: deleteOrganizationApplianceDnsLocalProfile](https://developer.cisco.com/meraki/api-v1/#!delete-organization-appliance-dns-local-profile)

        Args:
            organization_id: Organization ID.
            profile_id: Profile ID.

        Returns:
            Successful operation.

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        profile_id = urllib.parse.quote(str(profile_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/local/profiles/{profile_id}"

        return self._session.delete(
            scope="appliance", operation_id="deleteOrganizationApplianceDnsLocalProfile", path=path
        )

    def get_organization_appliance_dns_local_records(
        self, organization_id: str, *, profile_ids: list[str] | None = None
    ) -> PaginatedResponse[GetOrganizationApplianceDnsLocalRecordsResponseItem]:
        """Fetch the DNS records used in local DNS profiles.

        [API documentation: getOrganizationApplianceDnsLocalRecords](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-dns-local-records)

        Args:
            organization_id: Organization ID.
            profile_ids: Optional parameter to filter the results by profile IDs.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "recordId": "12345",
                "hostname": "www.test.com",
                "address": "10.1.1.0",
                "profile": {
                  "id": "1"
                }
              }
            ]
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/local/records"

        params: dict[str, Any] = {}
        if profile_ids is not None:
            params["profileIds[]"] = profile_ids

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceDnsLocalRecords",
            path=path,
            params=params,
            item_schema=GetOrganizationApplianceDnsLocalRecordsResponseItem,
        )

    def create_organization_appliance_dns_local_record(
        self,
        *,
        organization_id: str,
        hostname: str,
        address: str,
        profile: CreateOrganizationApplianceDnsLocalRecordProfile,
    ) -> CreateOrganizationApplianceDnsLocalRecordResponse:
        """Create a new local DNS record.

        [API documentation: createOrganizationApplianceDnsLocalRecord](https://developer.cisco.com/meraki/api-v1/#!create-organization-appliance-dns-local-record)

        Args:
            organization_id: Organization ID.
            hostname: Hostname for the DNS record.
            address: IP for the DNS record.
            profile: The profile the DNS record is associated with.

        Returns:
            Successful operation.

        Example API response:
            ```json
            [
              {
                "recordId": "12345",
                "hostname": "www.test.com",
                "address": "10.1.1.0",
                "profile": {
                  "id": "1"
                }
              }
            ]
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/local/records"

        payload: dict[str, Any] = {}
        if hostname is not None:
            payload["hostname"] = hostname
        if address is not None:
            payload["address"] = address
        if profile is not None:
            payload["profile"] = profile.model_dump(by_alias=True, exclude_none=True)

        return self._session.post(
            scope="appliance",
            operation_id="createOrganizationApplianceDnsLocalRecord",
            path=path,
            json=payload,
            response_schema=CreateOrganizationApplianceDnsLocalRecordResponse,
        )

    def update_organization_appliance_dns_local_record(
        self,
        *,
        organization_id: str,
        record_id: str,
        hostname: str | None = None,
        address: str | None = None,
        profile: UpdateOrganizationApplianceDnsLocalRecordProfile | None = None,
    ) -> UpdateOrganizationApplianceDnsLocalRecordResponse:
        """Updates a local DNS record.

        [API documentation: updateOrganizationApplianceDnsLocalRecord](https://developer.cisco.com/meraki/api-v1/#!update-organization-appliance-dns-local-record)

        Args:
            organization_id: Organization ID.
            record_id: Record ID.
            hostname: Hostname for the DNS record.
            address: IP for the DNS record.
            profile: The profile the DNS record is associated with.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "recordId": "12345",
              "hostname": "www.test.com",
              "address": "10.1.1.0",
              "profile": {
                "id": "1"
              }
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        record_id = urllib.parse.quote(str(record_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/local/records/{record_id}"

        payload: dict[str, Any] = {}
        if hostname is not None:
            payload["hostname"] = hostname
        if address is not None:
            payload["address"] = address
        if profile is not None:
            payload["profile"] = profile.model_dump(by_alias=True, exclude_none=True)

        return self._session.put(
            scope="appliance",
            operation_id="updateOrganizationApplianceDnsLocalRecord",
            path=path,
            json=payload,
            response_schema=UpdateOrganizationApplianceDnsLocalRecordResponse,
        )

    def delete_organization_appliance_dns_local_record(
        self, *, organization_id: str, record_id: str
    ) -> None:
        """Deletes a local DNS record.

        [API documentation: deleteOrganizationApplianceDnsLocalRecord](https://developer.cisco.com/meraki/api-v1/#!delete-organization-appliance-dns-local-record)

        Args:
            organization_id: Organization ID.
            record_id: Record ID.

        Returns:
            Successful operation.

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        record_id = urllib.parse.quote(str(record_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/local/records/{record_id}"

        return self._session.delete(
            scope="appliance", operation_id="deleteOrganizationApplianceDnsLocalRecord", path=path
        )

    def get_organization_appliance_dns_split_profiles(
        self, organization_id: str, *, profile_ids: list[str] | None = None
    ) -> PaginatedResponse[GetOrganizationApplianceDnsSplitProfilesResponseItem]:
        """Fetch the split DNS profiles used in the organization.

        [API documentation: getOrganizationApplianceDnsSplitProfiles](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-dns-split-profiles)

        Args:
            organization_id: Organization ID.
            profile_ids: Optional parameter to filter the results by profile IDs.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "profileId": "123456",
                "name": "Default profile",
                "hostnames": [
                  "*.test1.com",
                  "*.test2.com"
                ],
                "nameservers": {
                  "addresses": [
                    "12.1.10.1"
                  ]
                }
              }
            ]
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/split/profiles"

        params: dict[str, Any] = {}
        if profile_ids is not None:
            params["profileIds[]"] = profile_ids

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceDnsSplitProfiles",
            path=path,
            params=params,
            item_schema=GetOrganizationApplianceDnsSplitProfilesResponseItem,
        )

    def create_organization_appliance_dns_split_profile(
        self,
        *,
        organization_id: str,
        name: str,
        hostnames: list[str],
        nameservers: CreateOrganizationApplianceDnsSplitProfileNameservers,
    ) -> CreateOrganizationApplianceDnsSplitProfileResponse:
        """Create a new split DNS profile.

        [API documentation: createOrganizationApplianceDnsSplitProfile](https://developer.cisco.com/meraki/api-v1/#!create-organization-appliance-dns-split-profile)

        Args:
            organization_id: Organization ID.
            name: Name of profile.
            hostnames: The hostname patterns to match for redirection. For more information on Split
                DNS hostname pattern formatting, please consult the Split DNS KB.
            nameservers: Contains the nameserver information for redirection.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "profileId": "123456",
              "name": "Default profile",
              "hostnames": [
                "*.test1.com",
                "*.test2.com"
              ],
              "nameservers": {
                "addresses": [
                  "12.1.10.1"
                ]
              }
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/split/profiles"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name
        if hostnames is not None:
            payload["hostnames"] = hostnames
        if nameservers is not None:
            payload["nameservers"] = nameservers.model_dump(by_alias=True, exclude_none=True)

        return self._session.post(
            scope="appliance",
            operation_id="createOrganizationApplianceDnsSplitProfile",
            path=path,
            json=payload,
            response_schema=CreateOrganizationApplianceDnsSplitProfileResponse,
        )

    def get_organization_appliance_dns_split_profiles_assignments(
        self,
        organization_id: str,
        *,
        profile_ids: list[str] | None = None,
        network_ids: list[str] | None = None,
    ) -> PaginatedResponse[GetOrganizationApplianceDnsSplitProfilesAssignmentsResponseItemsItem]:
        """Fetch the split DNS profile assignments in the organization.

        [API documentation: getOrganizationApplianceDnsSplitProfilesAssignments](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-dns-split-profiles-assignments)

        Args:
            organization_id: Organization ID.
            profile_ids: Optional parameter to filter the results by profile IDs.
            network_ids: Optional parameter to filter the results by network IDs.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            {
              "items": [
                {
                  "assignmentId": "123456",
                  "network": {
                    "id": "N_123456"
                  },
                  "profile": {
                    "id": "1234"
                  }
                }
              ],
              "meta": {
                "counts": {
                  "items": {
                    "total": 10,
                    "remaining": 0
                  }
                }
              }
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/split/profiles/assignments"

        params: dict[str, Any] = {}
        if profile_ids is not None:
            params["profileIds[]"] = profile_ids
        if network_ids is not None:
            params["networkIds[]"] = network_ids

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceDnsSplitProfilesAssignments",
            path=path,
            params=params,
            item_schema=GetOrganizationApplianceDnsSplitProfilesAssignmentsResponseItemsItem,
        )

    def create_organization_appliance_dns_split_profiles_assignments_bulk_create(
        self,
        *,
        organization_id: str,
        items: list[CreateOrganizationApplianceDnsSplitProfilesAssignmentsBulkCreateItemsItem],
    ) -> CreateOrganizationApplianceDnsSplitProfilesAssignmentsBulkCreateResponse:
        """Assign the split DNS profile to networks in the organization.

        [API documentation: createOrganizationApplianceDnsSplitProfilesAssignmentsBulkCreate](https://developer.cisco.com/meraki/api-v1/#!create-organization-appliance-dns-split-profiles-assignments-bulk-create)

        Args:
            organization_id: Organization ID.
            items: List containing the network ID and Profile ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "items": [
                {
                  "assignmentId": "123456",
                  "network": {
                    "id": "N_123456"
                  },
                  "profile": {
                    "id": "1234"
                  }
                }
              ]
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = (
            f"/organizations/{organization_id}/appliance/dns/split/profiles/assignments/bulkCreate"
        )

        payload: dict[str, Any] = {}
        if items is not None:
            payload["items"] = [item.model_dump(by_alias=True, exclude_none=True) for item in items]

        return self._session.post(
            scope="appliance",
            operation_id="createOrganizationApplianceDnsSplitProfilesAssignmentsBulkCreate",
            path=path,
            json=payload,
            response_schema=CreateOrganizationApplianceDnsSplitProfilesAssignmentsBulkCreateResponse,
        )

    def create_organization_appliance_dns_split_profiles_assignments_bulk_delete(
        self,
        *,
        organization_id: str,
        items: list[CreateOrganizationApplianceDnsSplitProfilesAssignmentsBulkDeleteItemsItem],
    ) -> CreateOrganizationApplianceDnsSplitProfilesAssignmentsBulkDeleteResponse:
        """Unassign the split DNS profile to networks in the organization.

        [API documentation: createOrganizationApplianceDnsSplitProfilesAssignmentsBulkDelete](https://developer.cisco.com/meraki/api-v1/#!create-organization-appliance-dns-split-profiles-assignments-bulk-delete)

        Args:
            organization_id: Organization ID.
            items: List containing the assignment ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "items": [
                {
                  "assignmentId": "123456",
                  "network": {
                    "id": "N_123456"
                  },
                  "profile": {
                    "id": "1234"
                  }
                }
              ]
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = (
            f"/organizations/{organization_id}/appliance/dns/split/profiles/assignments/bulkDelete"
        )

        payload: dict[str, Any] = {}
        if items is not None:
            payload["items"] = [item.model_dump(by_alias=True, exclude_none=True) for item in items]

        return self._session.post(
            scope="appliance",
            operation_id="createOrganizationApplianceDnsSplitProfilesAssignmentsBulkDelete",
            path=path,
            json=payload,
            response_schema=CreateOrganizationApplianceDnsSplitProfilesAssignmentsBulkDeleteResponse,
        )

    def update_organization_appliance_dns_split_profile(
        self,
        *,
        organization_id: str,
        profile_id: str,
        name: str | None = None,
        hostnames: list[str] | None = None,
        nameservers: UpdateOrganizationApplianceDnsSplitProfileNameservers | None = None,
    ) -> UpdateOrganizationApplianceDnsSplitProfileResponse:
        """Update a split DNS profile.

        [API documentation: updateOrganizationApplianceDnsSplitProfile](https://developer.cisco.com/meraki/api-v1/#!update-organization-appliance-dns-split-profile)

        Args:
            organization_id: Organization ID.
            profile_id: Profile ID.
            name: Name of profile.
            hostnames: The hostname patterns to match for redirection. For more information on Split
                DNS hostname pattern formatting, please consult the Split DNS KB.
            nameservers: Contains the nameserver information for redirection.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "profileId": "123456",
              "name": "Default profile",
              "hostnames": [
                "*.test1.com",
                "*.test2.com"
              ],
              "nameservers": {
                "addresses": [
                  "12.1.10.1"
                ]
              }
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        profile_id = urllib.parse.quote(str(profile_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/split/profiles/{profile_id}"

        payload: dict[str, Any] = {}
        if name is not None:
            payload["name"] = name
        if hostnames is not None:
            payload["hostnames"] = hostnames
        if nameservers is not None:
            payload["nameservers"] = nameservers.model_dump(by_alias=True, exclude_none=True)

        return self._session.put(
            scope="appliance",
            operation_id="updateOrganizationApplianceDnsSplitProfile",
            path=path,
            json=payload,
            response_schema=UpdateOrganizationApplianceDnsSplitProfileResponse,
        )

    def delete_organization_appliance_dns_split_profile(
        self, *, organization_id: str, profile_id: str
    ) -> None:
        """Deletes a split DNS profile.

        [API documentation: deleteOrganizationApplianceDnsSplitProfile](https://developer.cisco.com/meraki/api-v1/#!delete-organization-appliance-dns-split-profile)

        Args:
            organization_id: Organization ID.
            profile_id: Profile ID.

        Returns:
            Successful operation.

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        profile_id = urllib.parse.quote(str(profile_id), safe="")
        path = f"/organizations/{organization_id}/appliance/dns/split/profiles/{profile_id}"

        return self._session.delete(
            scope="appliance", operation_id="deleteOrganizationApplianceDnsSplitProfile", path=path
        )

    def get_organization_appliance_firewall_multicast_forwarding_by_network(
        self,
        organization_id: str,
        *,
        per_page: int | None = None,
        starting_after: str | None = None,
        ending_before: str | None = None,
        network_ids: list[str] | None = None,
        total_pages: int | Literal["all"] = "all",
        direction: Literal["prev", "next"] = "next",
    ) -> PaginatedResponse[
        GetOrganizationApplianceFirewallMulticastForwardingByNetworkResponseItemsItem
    ]:
        """List Static Multicasting forwarding settings for MX networks.

        [API documentation: getOrganizationApplianceFirewallMulticastForwardingByNetwork](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-firewall-multicast-forwarding-by-network)

        Args:
            organization_id: Organization ID.
            per_page: The number of entries per page returned. Acceptable range is 3 - 1000. Default
                is 1000.
            starting_after: A token used by the server to indicate the start of the page. Often this
                is a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            ending_before: A token used by the server to indicate the end of the page. Often this is
                a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            network_ids: Optional parameter to filter the results by network IDs.
            total_pages: use with per_page to get total results up to total_pages * per_page; -1 or
                "all" for all pages.
            direction: direction to paginate, either "next" (default) or "prev" page.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            {
              "items": [
                {
                  "network": {
                    "id": "N_24329156",
                    "name": "Main Office"
                  },
                  "rules": [
                    {
                      "description": "test",
                      "address": "224.0.0.1",
                      "vlanIds": [
                        "1"
                      ]
                    }
                  ]
                }
              ],
              "meta": {
                "counts": {
                  "items": {
                    "total": 1,
                    "remaining": 0
                  }
                }
              }
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/firewall/multicastForwarding/byNetwork"

        params: dict[str, Any] = {}
        if per_page is not None:
            params["perPage"] = per_page
        if starting_after is not None:
            params["startingAfter"] = starting_after
        if ending_before is not None:
            params["endingBefore"] = ending_before
        if network_ids is not None:
            params["networkIds[]"] = network_ids

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceFirewallMulticastForwardingByNetwork",
            path=path,
            params=params,
            total_pages=total_pages,
            direction=direction,
            item_schema=GetOrganizationApplianceFirewallMulticastForwardingByNetworkResponseItemsItem,
        )

    def get_organization_appliance_security_events(
        self,
        organization_id: str,
        *,
        t0: str | None = None,
        t1: str | None = None,
        timespan: float | None = None,
        per_page: int | None = None,
        starting_after: str | None = None,
        ending_before: str | None = None,
        sort_order: GetOrganizationApplianceSecurityEventsSortOrder | None = None,
        total_pages: int | Literal["all"] = "all",
        direction: Literal["prev", "next"] = "next",
    ) -> PaginatedResponse[GetOrganizationApplianceSecurityEventsResponse]:
        """List the security events for an organization.

        [API documentation: getOrganizationApplianceSecurityEvents](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-security-events)

        Args:
            organization_id: Organization ID.
            t0: The beginning of the timespan for the data. Data is gathered after the specified t0
                value. The maximum lookback period is 365 days from today.
            t1: The end of the timespan for the data. t1 can be a maximum of 365 days after t0.
            timespan: The timespan for which the information will be fetched. If specifying
                timespan, do not specify parameters t0 and t1. The value must be in
                seconds and be less than or equal to 365 days. The default is 31 days.
            per_page: The number of entries per page returned. Acceptable range is 3 - 1000. Default
                is 100.
            starting_after: A token used by the server to indicate the start of the page. Often this
                is a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            ending_before: A token used by the server to indicate the end of the page. Often this is
                a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            sort_order: Sorted order of security events based on event detection time. Order options
                are 'ascending' or 'descending'. Default is ascending order.
            total_pages: use with per_page to get total results up to total_pages * per_page; -1 or
                "all" for all pages.
            direction: direction to paginate, either "next" (default) or "prev" page.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "ts": "2018-02-11T00:00:00.090210Z",
                "eventType": "File Scanned",
                "clientName": "COMPUTER-M-V78J",
                "clientMac": "10:dd:b1:eb:88:f8",
                "clientIp": "192.168.128.2",
                "srcIp": "192.168.128.2",
                "destIp": "119.192.233.48",
                "protocol": "http",
                "uri": "http://www.favorite-icons.com/program/FavoriteIconsUninstall.exe",
                "canonicalName": "PUA.Win.Dropper.Kraddare::1201",
                "destinationPort": 80,
                "fileHash": "3ec1b9a95fe62aa25fc959643a0f227b76d253094681934daaf628d3574b3463",
                "fileType": "MS_EXE",
                "fileSizeBytes": 193688,
                "disposition": "Malicious",
                "action": "Blocked"
              },
              {
                "ts": "2018-02-11T00:00:00.090210Z",
                "eventType": "IDS Alert",
                "deviceMac": "00:18:0a:01:02:03",
                "clientMac": "A1:B2:C3:D4:E5:F6",
                "srcIp": "1.2.3.4:34195",
                "destIp": "10.20.30.40:80",
                "protocol": "tcp/ip",
                "priority": "2",
                "classification": "4",
                "blocked": true,
                "message": "SERVER-WEBAPP JBoss JMX console access attempt",
                "signature": "1:21516:9",
                "sigSource": "",
                "ruleId": "meraki:intrusion/snort/GID/1/SID/26267"
              }
            ]
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/security/events"

        params: dict[str, Any] = {}
        if t0 is not None:
            params["t0"] = t0
        if t1 is not None:
            params["t1"] = t1
        if timespan is not None:
            params["timespan"] = timespan
        if per_page is not None:
            params["perPage"] = per_page
        if starting_after is not None:
            params["startingAfter"] = starting_after
        if ending_before is not None:
            params["endingBefore"] = ending_before
        if sort_order is not None:
            params["sortOrder"] = sort_order

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceSecurityEvents",
            path=path,
            params=params,
            total_pages=total_pages,
            direction=direction,
            item_schema=GetOrganizationApplianceSecurityEventsResponse,
        )

    def get_organization_appliance_security_intrusion(self, organization_id: str) -> DictResponse:
        """Returns all supported intrusion settings for an organization.

        [API documentation: getOrganizationApplianceSecurityIntrusion](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-security-intrusion)

        Args:
            organization_id: Organization ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "allowedRules": [
                {
                  "ruleId": "meraki:intrusion/snort/GID/01/SID/688",
                  "message": "SQL sa login failed"
                },
                {
                  "ruleId": "meraki:intrusion/snort/GID/01/SID/5805",
                  "message": "MALWARE-OTHER Trackware myway speedbar runtime detection - switch engines"
                }
              ]
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/security/intrusion"

        return self._session.get(
            scope="appliance",
            operation_id="getOrganizationApplianceSecurityIntrusion",
            path=path,
            response_schema=DictResponse,
        )

    def update_organization_appliance_security_intrusion(
        self,
        *,
        organization_id: str,
        allowed_rules: list[UpdateOrganizationApplianceSecurityIntrusionAllowedRulesItem],
    ) -> DictResponse:
        """Sets supported intrusion settings for an organization.

        [API documentation: updateOrganizationApplianceSecurityIntrusion](https://developer.cisco.com/meraki/api-v1/#!update-organization-appliance-security-intrusion)

        Args:
            organization_id: Organization ID.
            allowed_rules: Sets a list of specific SNORT signatures to allow.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "allowedRules": [
                {
                  "ruleId": "meraki:intrusion/snort/GID/01/SID/688",
                  "message": "SQL sa login failed"
                },
                {
                  "ruleId": "meraki:intrusion/snort/GID/01/SID/5805",
                  "message": "MALWARE-OTHER Trackware myway speedbar runtime detection - switch engines"
                }
              ]
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/security/intrusion"

        payload: dict[str, Any] = {}
        if allowed_rules is not None:
            payload["allowedRules"] = [
                item.model_dump(by_alias=True, exclude_none=True) for item in allowed_rules
            ]

        return self._session.put(
            scope="appliance",
            operation_id="updateOrganizationApplianceSecurityIntrusion",
            path=path,
            json=payload,
            response_schema=DictResponse,
        )

    def get_organization_appliance_traffic_shaping_vpn_exclusions_by_network(
        self,
        organization_id: str,
        *,
        per_page: int | None = None,
        starting_after: str | None = None,
        ending_before: str | None = None,
        network_ids: list[str] | None = None,
        total_pages: int | Literal["all"] = "all",
        direction: Literal["prev", "next"] = "next",
    ) -> PaginatedResponse[
        GetOrganizationApplianceTrafficShapingVpnExclusionsByNetworkResponseItemsItem
    ]:
        """Display VPN exclusion rules for MX networks.

        [API documentation: getOrganizationApplianceTrafficShapingVpnExclusionsByNetwork](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-traffic-shaping-vpn-exclusions-by-network)

        Args:
            organization_id: Organization ID.
            per_page: The number of entries per page returned. Acceptable range is 3 - 1000. Default
                is 50.
            starting_after: A token used by the server to indicate the start of the page. Often this
                is a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            ending_before: A token used by the server to indicate the end of the page. Often this is
                a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            network_ids: Optional parameter to filter the results by network IDs.
            total_pages: use with per_page to get total results up to total_pages * per_page; -1 or
                "all" for all pages.
            direction: direction to paginate, either "next" (default) or "prev" page.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            {
              "items": [
                {
                  "networkId": "N_24329156",
                  "networkName": "Main Office",
                  "custom": [
                    {
                      "protocol": "tcp",
                      "destination": "192.168.3.0/24",
                      "port": "8000"
                    }
                  ],
                  "majorApplications": [
                    {
                      "id": "meraki:vpnExclusion/application/2",
                      "name": "Office 365 Sharepoint"
                    }
                  ]
                }
              ]
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/trafficShaping/vpnExclusions/byNetwork"

        params: dict[str, Any] = {}
        if per_page is not None:
            params["perPage"] = per_page
        if starting_after is not None:
            params["startingAfter"] = starting_after
        if ending_before is not None:
            params["endingBefore"] = ending_before
        if network_ids is not None:
            params["networkIds[]"] = network_ids

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceTrafficShapingVpnExclusionsByNetwork",
            path=path,
            params=params,
            total_pages=total_pages,
            direction=direction,
            item_schema=GetOrganizationApplianceTrafficShapingVpnExclusionsByNetworkResponseItemsItem,
        )

    def get_organization_appliance_uplink_statuses(
        self,
        organization_id: str,
        *,
        per_page: int | None = None,
        starting_after: str | None = None,
        ending_before: str | None = None,
        network_ids: list[str] | None = None,
        serials: list[str] | None = None,
        iccids: list[str] | None = None,
        total_pages: int | Literal["all"] = "all",
        direction: Literal["prev", "next"] = "next",
    ) -> PaginatedResponse[GetOrganizationApplianceUplinkStatusesResponseItem]:
        """List the uplink status of every Meraki MX and Z series appliances in the organization.

        [API documentation: getOrganizationApplianceUplinkStatuses](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-uplink-statuses)

        Args:
            organization_id: Organization ID.
            per_page: The number of entries per page returned. Acceptable range is 3 - 1000. Default
                is 1000.
            starting_after: A token used by the server to indicate the start of the page. Often this
                is a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            ending_before: A token used by the server to indicate the end of the page. Often this is
                a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            network_ids: A list of network IDs. The returned devices will be filtered to only
                include these networks.
            serials: A list of serial numbers. The returned devices will be filtered to only include
                these serials.
            iccids: A list of ICCIDs. The returned devices will be filtered to only include these
                ICCIDs.
            total_pages: use with per_page to get total results up to total_pages * per_page; -1 or
                "all" for all pages.
            direction: direction to paginate, either "next" (default) or "prev" page.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "networkId": "N_24329156",
                "serial": "Q234-ABCD-5678",
                "model": "MX68C",
                "lastReportedAt": "2018-02-11T00:00:00Z",
                "highAvailability": {
                  "enabled": true,
                  "role": "primary"
                },
                "uplinks": [
                  {
                    "interface": "wan1",
                    "status": "active",
                    "ip": "1.2.3.4",
                    "gateway": "1.2.3.5",
                    "publicIp": "123.123.123.1",
                    "primaryDns": "8.8.8.8",
                    "secondaryDns": "8.8.4.4",
                    "ipAssignedBy": "static"
                  }
                ]
              }
            ]
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/uplink/statuses"

        params: dict[str, Any] = {}
        if per_page is not None:
            params["perPage"] = per_page
        if starting_after is not None:
            params["startingAfter"] = starting_after
        if ending_before is not None:
            params["endingBefore"] = ending_before
        if network_ids is not None:
            params["networkIds[]"] = network_ids
        if serials is not None:
            params["serials[]"] = serials
        if iccids is not None:
            params["iccids[]"] = iccids

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceUplinkStatuses",
            path=path,
            params=params,
            total_pages=total_pages,
            direction=direction,
            item_schema=GetOrganizationApplianceUplinkStatusesResponseItem,
        )

    def get_organization_appliance_uplinks_statuses_overview(
        self, organization_id: str, *, network_ids: list[str] | None = None
    ) -> GetOrganizationApplianceUplinksStatusesOverviewResponse:
        """Returns an overview of uplink statuses.

        [API documentation: getOrganizationApplianceUplinksStatusesOverview](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-uplinks-statuses-overview)

        Args:
            organization_id: Organization ID.
            network_ids: A list of network IDs. The returned devices will be filtered to only
                include these networks.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "counts": {
                "byStatus": {
                  "active": 5,
                  "ready": 5,
                  "failed": 4,
                  "connecting": 2,
                  "notConnected": 1
                }
              }
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/uplinks/statuses/overview"

        params: dict[str, Any] = {}
        if network_ids is not None:
            params["networkIds[]"] = network_ids

        return self._session.get(
            scope="appliance",
            operation_id="getOrganizationApplianceUplinksStatusesOverview",
            path=path,
            params=params,
            response_schema=GetOrganizationApplianceUplinksStatusesOverviewResponse,
        )

    def get_organization_appliance_uplinks_usage_by_network(
        self,
        organization_id: str,
        *,
        t0: str | None = None,
        t1: str | None = None,
        timespan: float | None = None,
    ) -> PaginatedResponse[GetOrganizationApplianceUplinksUsageByNetworkResponseItem]:
        """Get the sent and received bytes for each uplink of all MX and Z networks within an organization.

        [API documentation: getOrganizationApplianceUplinksUsageByNetwork](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-uplinks-usage-by-network)

        Args:
            organization_id: Organization ID.
            t0: The beginning of the timespan for the data. The maximum lookback period is 30 days
                from today.
            t1: The end of the timespan for the data. t1 can be a maximum of 14 days after t0.
            timespan: The timespan for which the information will be fetched. If specifying
                timespan, do not specify parameters t0 and t1. The value must be in
                seconds and be less than or equal to 14 days. The default is 1 day.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "networkId": "N_24329156",
                "name": "Main Office",
                "byUplink": [
                  {
                    "serial": "Q234-ABCD-5678",
                    "interface": "wan1",
                    "sent": 200,
                    "received": 400
                  }
                ]
              }
            ]
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/uplinks/usage/byNetwork"

        params: dict[str, Any] = {}
        if t0 is not None:
            params["t0"] = t0
        if t1 is not None:
            params["t1"] = t1
        if timespan is not None:
            params["timespan"] = timespan

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceUplinksUsageByNetwork",
            path=path,
            params=params,
            item_schema=GetOrganizationApplianceUplinksUsageByNetworkResponseItem,
        )

    def get_organization_appliance_vpn_site_to_site_ipsec_peers_slas(
        self, organization_id: str
    ) -> PaginatedResponse[GetOrganizationApplianceVpnSiteToSiteIpsecPeersSlasResponseItemsItem]:
        """Get the list of available IPsec SLA policies for an organization.

        [API documentation: getOrganizationApplianceVpnSiteToSiteIpsecPeersSlas](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-vpn-site-to-site-ipsec-peers-slas)

        Args:
            organization_id: Organization ID.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            {
              "items": [
                {
                  "id": "12345",
                  "name": "sla policy",
                  "uri": "http://checkthisendpoint.com",
                  "ipsec": {
                    "peerIds": [
                      "1010",
                      "1011"
                    ]
                  }
                }
              ],
              "meta": {
                "counts": {
                  "items": {
                    "total": 1,
                    "remaining": 0
                  }
                }
              }
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/vpn/siteToSite/ipsec/peers/slas"

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceVpnSiteToSiteIpsecPeersSlas",
            path=path,
            item_schema=GetOrganizationApplianceVpnSiteToSiteIpsecPeersSlasResponseItemsItem,
        )

    def update_organization_appliance_vpn_site_to_site_ipsec_peers_slas(
        self,
        organization_id: str,
        *,
        items: list[UpdateOrganizationApplianceVpnSiteToSiteIpsecPeersSlasItemsItem] | None = None,
    ) -> UpdateOrganizationApplianceVpnSiteToSiteIpsecPeersSlasResponse:
        """Update the IPsec SLA policies for an organization.

        [API documentation: updateOrganizationApplianceVpnSiteToSiteIpsecPeersSlas](https://developer.cisco.com/meraki/api-v1/#!update-organization-appliance-vpn-site-to-site-ipsec-peers-slas)

        Args:
            organization_id: Organization ID.
            items: List of IPsec SLA policies.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "items": [
                {
                  "id": "12345",
                  "name": "sla policy",
                  "uri": "http://checkthisendpoint.com",
                  "ipsec": {
                    "peerIds": [
                      "1010",
                      "1011"
                    ]
                  }
                }
              ],
              "meta": {
                "counts": {
                  "items": {
                    "total": 1,
                    "remaining": 0
                  }
                }
              }
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/vpn/siteToSite/ipsec/peers/slas"

        payload: dict[str, Any] = {}
        if items is not None:
            payload["items"] = [item.model_dump(by_alias=True, exclude_none=True) for item in items]

        return self._session.put(
            scope="appliance",
            operation_id="updateOrganizationApplianceVpnSiteToSiteIpsecPeersSlas",
            path=path,
            json=payload,
            response_schema=UpdateOrganizationApplianceVpnSiteToSiteIpsecPeersSlasResponse,
        )

    def get_organization_appliance_vpn_stats(
        self,
        organization_id: str,
        *,
        per_page: int | None = None,
        starting_after: str | None = None,
        ending_before: str | None = None,
        network_ids: list[str] | None = None,
        t0: str | None = None,
        t1: str | None = None,
        timespan: float | None = None,
        total_pages: int | Literal["all"] = "all",
        direction: Literal["prev", "next"] = "next",
    ) -> PaginatedResponse[GetOrganizationApplianceVpnStatsResponseItem]:
        """Show VPN history stat for networks in an organization.

        [API documentation: getOrganizationApplianceVpnStats](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-vpn-stats)

        Args:
            organization_id: Organization ID.
            per_page: The number of entries per page returned. Acceptable range is 3 - 300. Default
                is 300.
            starting_after: A token used by the server to indicate the start of the page. Often this
                is a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            ending_before: A token used by the server to indicate the end of the page. Often this is
                a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            network_ids: A list of Meraki network IDs to filter results to contain only specified
                networks. E.g.: networkIds[]=N_12345678&networkIds[]=L_3456.
            t0: The beginning of the timespan for the data. The maximum lookback period is 31 days
                from today.
            t1: The end of the timespan for the data. t1 can be a maximum of 31 days after t0.
            timespan: The timespan for which the information will be fetched. If specifying
                timespan, do not specify parameters t0 and t1. The value must be in
                seconds and be less than or equal to 31 days. The default is 1 day.
            total_pages: use with per_page to get total results up to total_pages * per_page; -1 or
                "all" for all pages.
            direction: direction to paginate, either "next" (default) or "prev" page.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "networkId": "N_24329156",
                "networkName": "Main Office",
                "merakiVpnPeers": [
                  {
                    "networkId": "N_12345678",
                    "networkName": "San Francisco Office",
                    "usageSummary": {
                      "receivedInKilobytes": 1234567,
                      "sentInKilobytes": 2345678
                    },
                    "latencySummaries": [
                      {
                        "senderUplink": "wan1",
                        "receiverUplink": "wan1",
                        "avgLatencyMs": 15,
                        "minLatencyMs": 10,
                        "maxLatencyMs": 30
                      }
                    ],
                    "lossPercentageSummaries": [
                      {
                        "senderUplink": "wan1",
                        "receiverUplink": "wan1",
                        "avgLossPercentage": 0.0,
                        "minLossPercentage": 0.0,
                        "maxLossPercentage": 0.1
                      }
                    ],
                    "jitterSummaries": [
                      {
                        "senderUplink": "wan1",
                        "receiverUplink": "wan1",
                        "avgJitter": 0.01,
                        "minJitter": 0.0,
                        "maxJitter": 0.25
                      }
                    ],
                    "mosSummaries": [
                      {
                        "senderUplink": "wan1",
                        "receiverUplink": "wan1",
                        "avgMos": 4.1,
                        "minMos": 4.0,
                        "maxMos": 4.2
                      }
                    ]
                  }
                ]
              }
            ]
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/vpn/stats"

        params: dict[str, Any] = {}
        if per_page is not None:
            params["perPage"] = per_page
        if starting_after is not None:
            params["startingAfter"] = starting_after
        if ending_before is not None:
            params["endingBefore"] = ending_before
        if network_ids is not None:
            params["networkIds[]"] = network_ids
        if t0 is not None:
            params["t0"] = t0
        if t1 is not None:
            params["t1"] = t1
        if timespan is not None:
            params["timespan"] = timespan

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceVpnStats",
            path=path,
            params=params,
            total_pages=total_pages,
            direction=direction,
            item_schema=GetOrganizationApplianceVpnStatsResponseItem,
        )

    def get_organization_appliance_vpn_statuses(
        self,
        organization_id: str,
        *,
        per_page: int | None = None,
        starting_after: str | None = None,
        ending_before: str | None = None,
        network_ids: list[str] | None = None,
        total_pages: int | Literal["all"] = "all",
        direction: Literal["prev", "next"] = "next",
    ) -> PaginatedResponse[GetOrganizationApplianceVpnStatusesResponseItem]:
        """Show VPN status for networks in an organization.

        [API documentation: getOrganizationApplianceVpnStatuses](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-vpn-statuses)

        Args:
            organization_id: Organization ID.
            per_page: The number of entries per page returned. Acceptable range is 3 - 300. Default
                is 300.
            starting_after: A token used by the server to indicate the start of the page. Often this
                is a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            ending_before: A token used by the server to indicate the end of the page. Often this is
                a timestamp or an ID but it is not limited to those. This parameter
                should not be defined by client applications. The link for the first,
                last, prev, or next page in the HTTP Link header should define it.
            network_ids: A list of Meraki network IDs to filter results to contain only specified
                networks. E.g.: networkIds[]=N_12345678&networkIds[]=L_3456.
            total_pages: use with per_page to get total results up to total_pages * per_page; -1 or
                "all" for all pages.
            direction: direction to paginate, either "next" (default) or "prev" page.

        Returns:
            Successful operation.

        Note:
            Returns a lazy PaginatedResponse
            that can be iterated or collected with `.collect()`.
            Page metadata is available on `.meta` and `.meta_pages`.

        Example API response:
            ```json
            [
              {
                "networkId": "N_12345678",
                "networkName": "San Francisco Office",
                "deviceSerial": "Q234-ABCD-1234",
                "deviceStatus": "online",
                "uplinks": [
                  {
                    "interface": "wan1",
                    "publicIp": "1.2.3.4"
                  }
                ],
                "vpnMode": "hub",
                "exportedSubnets": [
                  {
                    "subnet": "192.168.0.0/24",
                    "name": "sales-vlan"
                  }
                ],
                "merakiVpnPeers": [
                  {
                    "networkId": "L_1234",
                    "networkName": "New York Office",
                    "reachability": "reachable"
                  }
                ],
                "thirdPartyVpnPeers": [
                  {
                    "name": "Tokyo Office",
                    "publicIp": "222.111.222.111",
                    "reachability": "reachable"
                  }
                ]
              }
            ]
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/vpn/statuses"

        params: dict[str, Any] = {}
        if per_page is not None:
            params["perPage"] = per_page
        if starting_after is not None:
            params["startingAfter"] = starting_after
        if ending_before is not None:
            params["endingBefore"] = ending_before
        if network_ids is not None:
            params["networkIds[]"] = network_ids

        return self._session.get_pages(
            scope="appliance",
            operation_id="getOrganizationApplianceVpnStatuses",
            path=path,
            params=params,
            total_pages=total_pages,
            direction=direction,
            item_schema=GetOrganizationApplianceVpnStatusesResponseItem,
        )

    def get_organization_appliance_vpn_third_party_vpn_peers(
        self, organization_id: str
    ) -> GetOrganizationApplianceVpnThirdPartyVPNPeersResponse:
        """Return the third party VPN peers for an organization.

        [API documentation: getOrganizationApplianceVpnThirdPartyVPNPeers](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-vpn-third-party-vpn-peers)

        Args:
            organization_id: Organization ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "peers": [
                {
                  "peerId": "1234",
                  "name": "Peer Name",
                  "publicIp": "123.123.123.1",
                  "remoteId": "miles@meraki.com",
                  "localId": "myMXId@meraki.com",
                  "secret": "secret",
                  "privateSubnets": [
                    "192.168.1.0/24",
                    "192.168.128.0/24"
                  ],
                  "ipsecPolicies": {
                    "ikeCipherAlgo": [
                      "tripledes"
                    ],
                    "ikeAuthAlgo": [
                      "sha1"
                    ],
                    "ikePrfAlgo": [
                      "prfsha1"
                    ],
                    "ikeDiffieHellmanGroup": [
                      "group2"
                    ],
                    "ikeLifetime": 28800,
                    "childCipherAlgo": [
                      "aes128"
                    ],
                    "childAuthAlgo": [
                      "sha1"
                    ],
                    "childPfsGroup": [
                      "disabled"
                    ],
                    "childLifetime": 28800
                  },
                  "slaPolicy": {
                    "id": "1234"
                  },
                  "ipsecPoliciesPreset": "custom",
                  "ikeVersion": "1",
                  "networkTags": [
                    "all"
                  ],
                  "network": {
                    "names": [
                      "Network 1",
                      "Location 2",
                      "Network 3"
                    ],
                    "ids": [
                      "N_1",
                      "L_2",
                      "N_3"
                    ]
                  },
                  "isRouteBased": true,
                  "ebgpNeighbor": {
                    "neighborId": 1234,
                    "neighborIp": "10.10.10.22",
                    "ipVersion": 4,
                    "remoteAsNumber": 64343,
                    "ebgpHoldTimer": 180,
                    "ebgpMultihop": 2,
                    "sourceIp": "10.10.10.22",
                    "pathPrepend": [
                      1,
                      2
                    ],
                    "multiExitDiscriminator": 1,
                    "weight": 10
                  },
                  "priorityInGroup": 1,
                  "group": {
                    "number": 1,
                    "failover": {
                      "directToInternet": true
                    },
                    "activeActiveTunnel": true
                  }
                }
              ]
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/vpn/thirdPartyVPNPeers"

        return self._session.get(
            scope="appliance",
            operation_id="getOrganizationApplianceVpnThirdPartyVPNPeers",
            path=path,
            response_schema=GetOrganizationApplianceVpnThirdPartyVPNPeersResponse,
        )

    def update_organization_appliance_vpn_third_party_vpn_peers(
        self,
        *,
        organization_id: str,
        peers: list[UpdateOrganizationApplianceVpnThirdPartyVPNPeersPeersItem],
    ) -> UpdateOrganizationApplianceVpnThirdPartyVPNPeersResponse:
        """Update the third party VPN peers for an organization.

        [API documentation: updateOrganizationApplianceVpnThirdPartyVPNPeers](https://developer.cisco.com/meraki/api-v1/#!update-organization-appliance-vpn-third-party-vpn-peers)

        Args:
            organization_id: Organization ID.
            peers: The list of VPN peers.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "peers": [
                {
                  "peerId": "1234",
                  "name": "Peer Name",
                  "publicIp": "123.123.123.1",
                  "remoteId": "miles@meraki.com",
                  "localId": "myMXId@meraki.com",
                  "secret": "secret",
                  "privateSubnets": [
                    "192.168.1.0/24",
                    "192.168.128.0/24"
                  ],
                  "ipsecPolicies": {
                    "ikeCipherAlgo": [
                      "tripledes"
                    ],
                    "ikeAuthAlgo": [
                      "sha1"
                    ],
                    "ikePrfAlgo": [
                      "prfsha1"
                    ],
                    "ikeDiffieHellmanGroup": [
                      "group2"
                    ],
                    "ikeLifetime": 28800,
                    "childCipherAlgo": [
                      "aes128"
                    ],
                    "childAuthAlgo": [
                      "sha1"
                    ],
                    "childPfsGroup": [
                      "disabled"
                    ],
                    "childLifetime": 28800
                  },
                  "slaPolicy": {
                    "id": "1234"
                  },
                  "ipsecPoliciesPreset": "custom",
                  "ikeVersion": "1",
                  "networkTags": [
                    "all"
                  ],
                  "network": {
                    "names": [
                      "Network 1",
                      "Location 2",
                      "Network 3"
                    ],
                    "ids": [
                      "N_1",
                      "L_2",
                      "N_3"
                    ]
                  },
                  "isRouteBased": true,
                  "ebgpNeighbor": {
                    "neighborId": 1234,
                    "neighborIp": "10.10.10.22",
                    "ipVersion": 4,
                    "remoteAsNumber": 64343,
                    "ebgpHoldTimer": 180,
                    "ebgpMultihop": 2,
                    "sourceIp": "10.10.10.22",
                    "pathPrepend": [
                      1,
                      2
                    ],
                    "multiExitDiscriminator": 1,
                    "weight": 10
                  },
                  "priorityInGroup": 1,
                  "group": {
                    "number": 1,
                    "failover": {
                      "directToInternet": true
                    },
                    "activeActiveTunnel": true
                  }
                }
              ]
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/vpn/thirdPartyVPNPeers"

        payload: dict[str, Any] = {}
        if peers is not None:
            payload["peers"] = [item.model_dump(by_alias=True, exclude_none=True) for item in peers]

        return self._session.put(
            scope="appliance",
            operation_id="updateOrganizationApplianceVpnThirdPartyVPNPeers",
            path=path,
            json=payload,
            response_schema=UpdateOrganizationApplianceVpnThirdPartyVPNPeersResponse,
        )

    def get_organization_appliance_vpn_vpn_firewall_rules(
        self, organization_id: str
    ) -> GetOrganizationApplianceVpnVpnFirewallRulesResponse:
        """Return the firewall rules for an organization's site-to-site VPN.

        [API documentation: getOrganizationApplianceVpnVpnFirewallRules](https://developer.cisco.com/meraki/api-v1/#!get-organization-appliance-vpn-vpn-firewall-rules)

        Args:
            organization_id: Organization ID.

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "comment": "Allow TCP traffic to subnet with HTTP servers.",
                  "policy": "allow",
                  "protocol": "tcp",
                  "srcPort": "Any",
                  "srcCidr": "Any",
                  "destPort": "443",
                  "destCidr": "192.168.1.0/24",
                  "syslogEnabled": false
                }
              ]
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/vpn/vpnFirewallRules"

        return self._session.get(
            scope="appliance",
            operation_id="getOrganizationApplianceVpnVpnFirewallRules",
            path=path,
            response_schema=GetOrganizationApplianceVpnVpnFirewallRulesResponse,
        )

    def update_organization_appliance_vpn_vpn_firewall_rules(
        self,
        organization_id: str,
        *,
        rules: list[UpdateOrganizationApplianceVpnVpnFirewallRulesRulesItem] | None = None,
        syslog_default_rule: bool | None = None,
    ) -> UpdateOrganizationApplianceVpnVpnFirewallRulesResponse:
        """Update the firewall rules of an organization's site-to-site VPN.

        [API documentation: updateOrganizationApplianceVpnVpnFirewallRules](https://developer.cisco.com/meraki/api-v1/#!update-organization-appliance-vpn-vpn-firewall-rules)

        Args:
            organization_id: Organization ID.
            rules: An ordered array of the firewall rules (not including the default rule).
            syslog_default_rule: Log the special default rule (boolean value - enable only if you've
                configured a syslog server) (optional).

        Returns:
            Successful operation.

        Example API response:
            ```json
            {
              "rules": [
                {
                  "comment": "Allow TCP traffic to subnet with HTTP servers.",
                  "policy": "allow",
                  "protocol": "tcp",
                  "srcPort": "Any",
                  "srcCidr": "Any",
                  "destPort": "443",
                  "destCidr": "192.168.1.0/24",
                  "syslogEnabled": false
                }
              ]
            }
            ```

        """
        organization_id = urllib.parse.quote(str(organization_id), safe="")
        path = f"/organizations/{organization_id}/appliance/vpn/vpnFirewallRules"

        payload: dict[str, Any] = {}
        if rules is not None:
            payload["rules"] = [item.model_dump(by_alias=True, exclude_none=True) for item in rules]
        if syslog_default_rule is not None:
            payload["syslogDefaultRule"] = syslog_default_rule

        return self._session.put(
            scope="appliance",
            operation_id="updateOrganizationApplianceVpnVpnFirewallRules",
            path=path,
            json=payload,
            response_schema=UpdateOrganizationApplianceVpnVpnFirewallRulesResponse,
        )
