<script>
from dataclasses import dataclass
from abc import ABC, abstractmethod
from typing import List
import time


@dataclass
class Area:
    xl: float
    xr: float
    yb: float
    yt: float

    def serialize(self):
        return {
            "xl": self.xl,
            "xr": self.xr,
            "yb": self.yb,
            "yt": self.yt
        }


class Alert(ABC):
    @abstractmethod
    def RaiseAlert(self):
        pass

    @abstractmethod
    def ClearAlert(self):
        pass


class AreaDetector:
    def __init__(self, areas, alerts: List[Alert] = None, alert_delay_time=0):
        self.alerts = alerts
        self.alert_delay_time = alert_delay_time
        self.areas = []
        for area in areas:
            assert len(area) == 4, "Each area must be made of 4 values: [x left, x right, y bottom, y top]"
            self.areas.append(Area(*area))
        self.ok_areas = set()
        self.suspected_areas = {}
        self.alert_areas = set()
        self.alert_triggered = False

    def update(self, clusters_up_to_date):
        """
        To call every step.
        :param clusters_up_to_date: list of tracked clusters.
        """
        current_time = time.time()
        self.ok_areas.clear()
        for area in self.areas:
            suspect = False
            for cluster in clusters_up_to_date:
                x, y = cluster.centroid[cluster.width_axis], cluster.centroid[cluster.depth_axis]
                if area.xl <= x <= area.xr and area.yb <= y <= area.yt:
                    suspect = True
                    break
            self.adjust_tracking(current_time, area, suspect)
        self.handle_alerts()

    def adjust_tracking(self, current_time, area, suspect):
        if not suspect:
            self.suspected_areas.pop(area, None)
            if area in self.alert_areas:
                self.alert_areas.remove(area)
            self.ok_areas.add(area)
        elif area not in self.alert_areas:
            if area not in self.suspected_areas:
                self.suspected_areas[area] = current_time
            if current_time - self.suspected_areas[area] >= self.alert_delay_time:
                self.alert_areas.add(area)
                self.suspected_areas.pop(area)

    def handle_alerts(self):
        if self.alerts is None:
            return
        if len(self.alert_areas) > 0:
            if not self.alert_triggered:
                self.alert_triggered = True
                for alert in self.alerts:
                    alert.RaiseAlert()
        else:
            if self.alert_triggered:
                self.alert_triggered = False
                for alert in self.alerts:
                    alert.ClearAlert()

    def serialize(self):
        return {
            "ok_areas": [area.serialize() for area in self.ok_areas],
            "alert_areas": [area.serialize() for area in self.alert_areas]
        }


@dataclass
class Profile:
    name: str
    min_volume: float
    max_volume: float
    alert_delay_time: float
    alert: Alert

    def serialize(self):
        return {
            "name": self.name,
            "min_volume": self.min_volume,
            "max_volume": self.max_volume,
            "alert_delay_time": self.alert_delay_time
        }
</script>
Given the above script, write a "ProfileAreaDetector" class.
Each profile is responsible for its alert handling mechanism and alert delay timing.
The cluster class has a "get_volume" method.