def on_power_button_on(self, event, *args, **kwargs):
        """React to power button event by notifying engine of
        state changes associated with it"""

        asset_event = event.get_next_power_event()
        load_upd = {}
        extra_draw = 0.0
        online_psus = []

        asset_event.load.new = asset_event.calculate_load(
            self.state, self.state.input_voltage)

        # for each psu, check if
        for key in self._psu_sm:
            psu_sm = self._psu_sm[key]

            load_upd[key] = EventDataPair(psu_sm.load, psu_sm.load)
            if not psu_sm.status:
                extra_draw += psu_sm.draw_percentage
            else:
                load_upd[key].new = (
                    load_upd[key].new +
                    asset_event.load.new * psu_sm.draw_percentage)
                online_psus.append(key)

        extra_load = asset_event.load.new * extra_draw / len(online_psus)

        for key in online_psus:
            load_upd[key].new = load_upd[key].new + extra_load

        asset_event.streamed_load_updates = load_upd
        return asset_event
    def on_power_button_off(self, event, *args, **kwargs):
        """React to server shut down by setting up upstream load for
        server PSUs"""
        asset_event = event.get_next_power_event()
        load_upd = {}

        asset_event.load.new = asset_event.calculate_load(
            self.state, self.state.input_voltage)

        for key in self._psu_sm:
            psu_sm = self._psu_sm[key]

            load_upd[key] = EventDataPair(psu_sm.load, psu_sm.load)
            if not psu_sm.status or math.isclose(psu_sm.input_voltage, 0.0):
                continue

            load_upd[key].new = psu_sm.power_usage

        asset_event.streamed_load_updates = load_upd
        return asset_event
    def on_input_voltage_down(self, event, *args, **kwargs):
        asset_event = event.get_next_power_event(self)
        assert event.source_key in self._psu_sm

        # store state info of the PSU causing havoc, keep track
        # of alternative power sources
        e_src_psu = self._psu_sm[event.source_key]
        e_src_psu_offline = math.isclose(event.in_volt.new, 0.0)

        alt_power_present = False

        if not math.isclose(self.state.load * e_src_psu.draw_percentage,
                            e_src_psu.load):
            source_psu_own_load = asset_event.calculate_load(
                e_src_psu, event.in_volt.old)
        else:
            source_psu_own_load = 0

        # keep track of load updates for multi-psu servers
        load_upd = {}

        # initialize load for PSUs (as unchanged)
        for key in self._psu_sm:
            load_upd[key] = EventDataPair(self._psu_sm[key].load,
                                          self._psu_sm[key].load)

        load_upd[e_src_psu.key].new = (
            asset_event.calculate_load(self.state, event.in_volt.new) *
            e_src_psu.draw_percentage)

        # check alternative power sources
        # and leave this server online if present
        for psu_key in self._psu_sm:
            psu_sm = self._psu_sm[psu_key]

            # skip over source event psu or offline psus
            if psu_key == e_src_psu.key or not psu_sm.status:
                continue

            alt_power_present = True
            # no load redistribution happens if source is still online
            if not e_src_psu_offline:
                continue

            # distribute load to another PSU
            load_upd[psu_key].new = (load_upd[psu_key].new -
                                     load_upd[e_src_psu.key].difference -
                                     source_psu_own_load)

        # finalize asset state changes based off PSU and in-volt status
        if not alt_power_present and e_src_psu_offline:
            # state needs to change when all power sources are offline
            asset_event.state.new = self.power_off()

        # update server load if all PSUs are off or
        # if in voltage simply dropped (but not to zero)
        if not alt_power_present or not e_src_psu_offline:
            asset_event.calc_load_from_volt()
            self._update_load(self.state.load +
                              load_upd[e_src_psu.key].difference)

        asset_event.streamed_load_updates = load_upd

        return asset_event
    def on_input_voltage_up(self, event, *args, **kwargs):
        asset_event = event.get_next_power_event(self)
        assert event.source_key in self._psu_sm

        e_src_psu = self._psu_sm[event.source_key]

        # keep track of load updates for multi-psu servers
        load_upd = {}
        extra_draw = 0.0

        should_change_load = True
        should_power_up = (not self.state.status or
                           not self.state.vm_is_active()) and not math.isclose(
                               event.in_volt.new, 0.0)

        new_asset_load = asset_event.calculate_load(self.state,
                                                    event.in_volt.new)
        old_asset_load = asset_event.calculate_load(self.state,
                                                    event.in_volt.old)

        # initialize load for PSUs
        # and process alternative power sources (PSUs)
        for key in self._psu_sm:

            psu_sm = self._psu_sm[key]
            load_upd[key] = EventDataPair(0.0, 0.0)

            if psu_sm.key == event.source_key:
                continue

            # if alternative power source is off, grab extra load from it
            if not psu_sm.status:
                extra_draw += psu_sm.draw_percentage

            # if alternative power source is currently having extra load
            # that the volt event source asset is supposed to be drawing
            # then redistribute it back so that load is equal among all psus
            elif math.isclose(event.in_volt.old,
                              0.0) and self._psu_drawing_extra(psu_sm):
                # asset load should not change (we are just redistributing same load)
                load_upd[key].old = psu_sm.load
                load_upd[key].new = (
                    psu_sm.load - new_asset_load * e_src_psu.draw_percentage)
                should_change_load = False
            # add load to a psu due to server powering up
            elif should_power_up:
                load_upd[
                    psu_sm.key].new = new_asset_load * psu_sm.draw_percentage

        # set load for the PSU voltage event is associated with
        src_psu_draw = e_src_psu.draw_percentage + extra_draw
        load_upd[e_src_psu.key].old = old_asset_load * src_psu_draw
        load_upd[e_src_psu.key].new = new_asset_load * src_psu_draw

        # power up if server is offline & boot-on-power BIOS option is on
        if should_power_up and self.state.power_on_ac_restored:
            asset_event.state.new = self.power_up()
            self._update_load(self.state.power_consumption / event.in_volt.new)

        # update load if no state changes
        elif not should_power_up and should_change_load:
            asset_event.calc_load_from_volt()
            self._update_load(self.state.load +
                              load_upd[e_src_psu.key].difference)
        elif not self.state.status and not self.state.power_on_ac_restored:
            load_upd = {}

        asset_event.streamed_load_updates = load_upd
        return asset_event