Update zone statuses based on the current message.

        :param message: message to use to update the zone tracking
        :type message: :py:class:`~alarmdecoder.messages.Message` or :py:class:`~alarmdecoder.messages.ExpanderMessage`

def update(self, message):
        """
        Update zone statuses based on the current message.

        :param message: message to use to update the zone tracking
        :type message: :py:class:`~alarmdecoder.messages.Message` or :py:class:`~alarmdecoder.messages.ExpanderMessage`
        """
        if isinstance(message, ExpanderMessage):
            zone = -1

            if message.type == ExpanderMessage.ZONE:
                zone = self.expander_to_zone(message.address, message.channel, self.alarmdecoder_object.mode)

            if zone != -1:
                status = Zone.CLEAR
                if message.value == 1:
                    status = Zone.FAULT
                elif message.value == 2:
                    status = Zone.CHECK

                # NOTE: Expander zone faults are handled differently than
                #       regular messages.  We don't include them in
                #       self._zones_faulted because they are not reported
                #       by the panel in it's rolling list of faults.
                try:
                    self._update_zone(zone, status=status)

                except IndexError:
                    self._add_zone(zone, status=status, expander=True)

        else:
            # Panel is ready, restore all zones.
            #
            # NOTE: This will need to be updated to support panels with
            #       multiple partitions.  In it's current state a ready on
            #       partition #1 will end up clearing all zones, even if they
            #       exist elsewhere and it shouldn't.
            #
            # NOTE: SYSTEM messages provide inconsistent ready statuses.  This
            #       may need to be extended later for other panels.
            if message.ready and not message.text.startswith("SYSTEM"):
                for zone in self._zones_faulted:
                    self._update_zone(zone, Zone.CLEAR)

                self._last_zone_fault = 0

            # Process fault
            elif self.alarmdecoder_object.mode != DSC and (message.check_zone or message.text.startswith("FAULT") or message.text.startswith("ALARM")):
                zone = message.parse_numeric_code()

                # NOTE: Odd case for ECP failures.  Apparently they report as
                #       zone 191 (0xBF) regardless of whether or not the
                #       3-digit mode is enabled... so we have to pull it out
                #       of the alpha message.
                if zone == 191:
                    zone_regex = re.compile('^CHECK (\d+).*$')

                    match = zone_regex.match(message.text)
                    if match is None:
                        return

                    zone = match.group(1)

                # Add new zones and clear expired ones.
                if zone in self._zones_faulted:
                    self._update_zone(zone)
                    self._clear_zones(zone)

                else:
                    status = Zone.FAULT
                    if message.check_zone:
                        status = Zone.CHECK

                    self._add_zone(zone, status=status)
                    self._zones_faulted.append(zone)
                    self._zones_faulted.sort()

                # Save our spot for the next message.
                self._last_zone_fault = zone

            self._clear_expired_zones()