Add an event to the roast log.

        This method should be used for registering events that may be worth
        tracking like first crack, second crack and the dropping of coffee.
        Similar to the standard reading output from the roaster, manually
        created events will include the current configuration reading, time and
        metadata passed in.

        :param event: Details describing what happened
        :type event: dict
        :returns: dict

def add_roast_event(self, event):
        """Add an event to the roast log.

        This method should be used for registering events that may be worth
        tracking like first crack, second crack and the dropping of coffee.
        Similar to the standard reading output from the roaster, manually
        created events will include the current configuration reading, time and
        metadata passed in.

        :param event: Details describing what happened
        :type event: dict
        :returns: dict
        """
        event_time = self.get_roast_time()

        def get_valid_config():
            """Keep grabbing configs until we have a valid one.

            In rare cases, the configuration will be invalid when the user
            registers an event. This malformation can occur across several
            events, so we use this helper to find a valid config to associate
            to the event while preserving the original time. Due to fast
            interval checking, this is not liable to skew data that much and
            it's better than extreme false data.
            """
            config = self.get_roast_properties()['last']['config']
            if not config['valid']:
                self._log.debug("Invalid config at event time, retrying...")
                self.get_valid_config()
            return config

        event.update({'time': event_time,
                      'config': get_valid_config()})
        self._roast['events'].append(event)
        return self.get_roast_properties()