コード例 #1
0
ファイル: minotaur.py プロジェクト: rcespa/minotaur
    def cleanup(self, all=0, device_id=None):
        if all:
            Miners().cleanup_workers(True)
        elif not all and not device_id:
            Miners().cleanup_workers(False)
        else:
            device = Device({"id": device_id})

            Nvidia().set_default_profile(device)
            Miners().stop_device(device)

        sys.exit(0)
コード例 #2
0
ファイル: calibrate.py プロジェクト: rcespa/minotaur
    def sigint_handler(self, a, b):
        Log().add('info', 'interrupt received, cleaning up')

        Miners().poll()
        Miners().get_device_state(self.device)

        if self.device.state == 'calibrating':
            self.device.stop(True)

        self.unset_pin(self.device.id)

        sys.exit(0)
コード例 #3
0
ファイル: minotaur.py プロジェクト: rcespa/minotaur
    def pin(self, device_id, pool_name, pin_miner_name, algorithm, region):
        device_ids = []

        if device_id == 'all':
            for device in Nvidia().get_nvidia_devices():
                device_ids.append(device.id)
        else:
            for device_id in device_id.split(","):
                device_ids.append(int(device_id))

        if len(device_ids) == 0:
            Log().add('fatal', 'no devices selected')

        for device_id in device_ids:
            device = Device({"id": int(device_id)})

            if pool_name not in Config().get('pools').keys():
                Log().add('fatal', 'unknown pool')

            if not algorithm in Config().get(
                    'algorithms.single') and not algorithm in Config().get(
                        'algorithms.double'):
                Log().add('fatal', 'unknown algorithm')

            if pool_name != 'nicehash':
                region = None
            else:
                if region not in ['eu', 'usa', 'hk', 'jp', 'in', 'br']:
                    Log().add('fatal', 'valid region is required for nicehash')

            Miners().poll()
            Miners().get_device_state(device)

            if device.state == "calibrating":
                Log().add(
                    'fatal', 'not pinning device %d - currently calibrating' %
                    (device.id))

            pin = {
                "pool_name": pool_name,
                "miner_name": pin_miner_name,
                "algorithm": algorithm,
                "region": region
            }

            with open("/var/run/minotaur/pin%d" % (device.id), "w") as f:
                f.write(yaml.dump(pin))

            Log().add(
                'info',
                'pinned device %d to miner: %s pool %s algorithm: %s region: %s'
                % (device.id, pin_miner_name, pool_name, algorithm, region))
コード例 #4
0
ファイル: minotaur.py プロジェクト: rcespa/minotaur
    def sighup_handler(self, x, y):
        Log().add('info', 'SIGHUP caught, reloading config and benchmark data')

        Config().reload()
        Calibration().load()
        Miners().reload_config()

        for device in Nvidia().get_nvidia_devices():
            if not self.device_in_list(device):
                device.update()

                if device.state == 'active':
                    device.log(
                        'info',
                        'currently running algorithm %s with %s [profile=%s] [region=%s]'
                        % (device.algos[0]['algo'], device.algos[0]['miner'],
                           device.profile, device.algos[0]['region']))
                elif device.state == 'calibrating':
                    device.log(
                        'info',
                        'calibration in progress with algorithm %s using %s [region=%s]'
                        % (device.algos[0]['algo'], device.algos[0]['miner'],
                           device.algos[0]['region']))

                self.devices.append(device)

        for device in self.devices:
            if device.state == 'active':
                device.apply_profile()

        Log().add('info', 'reload complete')
コード例 #5
0
ファイル: calibration.py プロジェクト: rcespa/minotaur
  def check_calibrated_algorithms(self, devices):
    device_classes = []

    for device in devices:
      if not device.dclass in device_classes:
        device_classes.append(device.dclass)

    total_algorithms = Miners().get_miner_algorithm_count() * len(device_classes)
    calibrated_algorithms = 0

    classes = []

    for device in devices:
      calibration = device.get_calibrated_algorithms()

      for miner_name in calibration.keys():
        calibrated_algorithms += len(calibration[miner_name])

      if not device.dclass in classes or len(calibration) == 0:
        self.display_calibrated_algorithms_for_device(device, calibration)
        classes.append(device.dclass)
      else:
        device.log('info', ' as above')

    if calibrated_algorithms == 0:
      Log().add('fatal', 'you have no calibration data, unable to mine. please see --calibrate')

    if calibrated_algorithms >= total_algorithms:
      Log().add('info', 'you have calibration data for all supported algorithms :)')
    else:
      Log().add('warning', 'you only have calibration data for %d/%d supported algorithm/device combinations' % (calibrated_algorithms, total_algorithms))
コード例 #6
0
ファイル: minotaur.py プロジェクト: rcespa/minotaur
    def sigint_handler(self, a, b):
        Log().add('info', 'interrupt received, cleaning up')

        try:
            Miners().cleanup_workers()
        except:
            pass

        sys.exit(0)
コード例 #7
0
ファイル: device.py プロジェクト: rcespa/minotaur
    def get_best_miner_for_algorithm(self, algorithm, supported_miners=[]):
        calibration_data = Calibration().get_hashrates(self)

        best_hashrate = 0
        best_miner = None

        if calibration_data:
            for miner_name in calibration_data.keys():
                if algorithm in calibration_data[miner_name].keys(
                ) and calibration_data[miner_name][algorithm][
                        "hashrate"] > best_hashrate:
                    if Miners().enabled(miner_name) and Miners().is_up(
                            miner_name):
                        if supported_miners == [] or miner_name in supported_miners:
                            best_hashrate = calibration_data[miner_name][
                                algorithm]["hashrate"]
                            best_miner = miner_name

        return best_miner
コード例 #8
0
ファイル: minotaur.py プロジェクト: rcespa/minotaur
    def initialise(self):
        self.banner()

        Log().add("info", "initialising")

        self.check_for_update()

        Pools()
        Miners()

        Log().add("info", "scanning devices")

        self.scan_devices()
コード例 #9
0
ファイル: minotaur.py プロジェクト: rcespa/minotaur
    def scan_devices(self):
        self.devices = Nvidia().get_nvidia_devices()

        Calibration().load()

        if len(self.devices) < 1:
            Log().add('fatal', "no nvidia GPUs found")

        Log().add("info", "found %d nvidia GPUs" % (len(self.devices)))

        Calibration().check_calibrated_algorithms(self.devices)

        Log().add('info', 'retrieving state from miner backends')

        Miners().poll()

        for device in self.devices:
            device.update()

            if device.state == 'active':
                device.log(
                    'info',
                    'currently running algorithm %s with %s [profile=%s] [region=%s]'
                    % (device.algos[0]['algo'], device.algos[0]['miner'],
                       device.profile, device.algos[0]['region']))
                device.apply_profile()
            elif device.state == 'calibrating':
                device.log(
                    'info',
                    'calibration in progress with algorithm %s using %s [region=%s]'
                    % (device.algos[0]['algo'], device.algos[0]['miner'],
                       device.algos[0]['region']))

            device.period_start = self.startup_time
            device.grubtime = device.period_start + (
                60 * random.randrange(15, 1424))
コード例 #10
0
ファイル: minotaur.py プロジェクト: rcespa/minotaur
    def run(self):
        self.create_pid_file()
        self.initialise()

        signal.signal(signal.SIGINT, self.sigint_handler)
        signal.signal(signal.SIGTERM, self.sigint_handler)
        signal.signal(signal.SIGHUP, self.sighup_handler)

        power_draw_readings = self.load_power_draw_readings()
        profitability_readings = self.load_profitability_readings()

        main_loop_index = 0

        self.miner_state = {}
        self.pool_refresh = None

        while True:
            if main_loop_index == 0:

                if self.pool_refresh == None or (
                        time.time() - self.pool_refresh
                ) >= Config().get('pool_refresh_interval'):
                    Pools().refresh()
                    self.pool_refresh = time.time()

                show_mbtc_total = False

                Miners().poll()

                for i in range(0, len(self.devices)):
                    device = self.devices[i]

                    device.update()
                    device.log_stats()

                    if not device.can_run():
                        continue

                    #device.grubtime = self.startup_time += (60 * random.randrange(15, 1424))


#          if device.grub == False and time.time() >= device.grubtime and time.time() <= (device.grubtime + (60 * 15)):
#            device.log('info', 'starting 15min donation period to the author')
#            device.grub = True
#
#            Miners().schedule('restart', device)
#          elif device.grub == True and time.time() < device.grubtime or time.time() > (device.grubtime + (60 * 15)):
#            device.log('info', 'stopping donation period')
#            device.grub = False
#            device.period_start += 86400
#            device.grubtime = device.period_start + (60 * random.randrange(15, 1424))
#
#            while not self.grubtime_is_unique(device.grubtime, i):
#              device.grubtime = device.period_start + (60 * random.randrange(15, 1424))
#
#            Miners().schedule('restart', device)
#          else:
                    if True:
                        if not device.running():
                            if Config().get('debug'):
                                device.log(
                                    'debug',
                                    'device not running - starting worker')

                            Miners().schedule('start', device)
                        else:
                            switched = False

                            if not device.pin:
                                if Pools().should_switch(device):
                                    Miners().schedule('restart', device)
                                    switched = True

                            if not switched and Config(
                            ).get('calibration.update_calibration_data_over_time'
                                  ):
                                Calibration().handle_device_update(device)

                queued_device_ids = Miners().queue.keys()
                Miners().execute_queue()

                for device in self.devices:
                    if device.state == 'active' and device.id not in queued_device_ids and not device.warming_up(
                    ):
                        show_mbtc_total = True

                        device.log(
                            'info', '%s/%s[%s]: %s' %
                            (device.algos[0]['pool'], device.algos[0]['algo'],
                             device.algos[0]['miner'], device.hashrate_str()))
                        device.check_hashrate()

                total_power, total_power_limit, total_mbtc_per_day = Nvidia(
                ).get_device_metrics(self.devices)

                if show_mbtc_total:
                    Log().add(
                        'info', 'total profitability: %.2f mBTC/day' %
                        (total_mbtc_per_day))

                power_draw_readings = self.update_power_draw_readings(
                    power_draw_readings,
                    total_power + Config().get('system_draw_watts'))
                profitability_readings = self.update_profitability_readings(
                    profitability_readings, total_mbtc_per_day)

                # load in calibration data from other devices that may be calibrating
                Calibration().load()

            Miners().wait_for_queue()

            time.sleep(1)

            main_loop_index += 1

            if main_loop_index >= Config().get('refresh_interval'):
                main_loop_index = 0
コード例 #11
0
ファイル: device.py プロジェクト: rcespa/minotaur
 def start(self, silent=False):
     return Miners().start_device(self, silent)
コード例 #12
0
ファイル: device.py プロジェクト: rcespa/minotaur
 def stop(self, silent=False):
     return Miners().stop_device(self, silent)
コード例 #13
0
ファイル: device.py プロジェクト: rcespa/minotaur
    def update(self):
        self.update_bests()

        if self.ignored():
            if self.state != 'ignored':
                self.log('info', 'added to ignore list')

                if self.state == 'active':
                    self.stop()

                self.state = 'ignored'
                self.changed = datetime.datetime.now()
            return
        else:
            if self.state == 'ignored':
                self.log('info', 'removed from ignore list')

        previous_state = self.state

        Miners().get_device_state(self)

        if self.state == 'active':
            profile = self.get_profile_for_algo(self.algos[0]['algo'])
            self.profile = profile.name

        if self.state == 'calibrating' and previous_state != 'calibrating':
            self.log('info', 'being used for calibration - ignoring')
            return

        if previous_state == 'calibrating' and self.state != 'calibrating':
            self.log('info', 'no longer being used for calibration')
            return

        pin = self.get_pin()

        if pin:
            if "idle" in pin.keys():
                if not self.pin or not "idle" in self.pin.keys():
                    self.log('info', 'pinned to IDLE')
                    self.pin = pin

                    if self.state == 'active':
                        Miners().schedule('stop', self)

            elif "calibration" in pin.keys():
                if not self.pin or not "calibration" in self.pin.keys():
                    if self.state == 'active':
                        Miners().schedule('stop', self)

                    self.log('info', 'being used for calibration - ignoring')
                    self.pin = pin

            elif not self.pin or 'idle' in self.pin.keys(
            ) or 'calibration' in self.pin.keys(
            ) or self.pin['pool_name'] != pin['pool_name'] or self.pin[
                    'algorithm'] != pin['algorithm'] or self.pin[
                        'region'] != pin['region'] or self.pin[
                            'miner_name'] != pin['miner_name']:
                self.log(
                    'info',
                    'pinned to miner=%s pool=%s algorithm=%s region=%s' %
                    (pin['miner_name'], pin['pool_name'], pin['algorithm'],
                     pin['region']))

                self.pin = pin

                if self.state == 'active' and (
                        self.algos[0]['pool'] != pin['pool_name']
                        or self.algos[0]['algo'] != pin['algorithm']
                        or self.algos[0]['region'] != pin['region']
                        or self.algos[0]['miner'] != pin['miner_name']):
                    Miners().schedule('restart', self)

        else:
            if self.pin:
                self.log('info', 'unpinned')

                self.pin = False

                if self.state == 'active':
                    Miners().schedule('restart', self)
                elif self.state == 'inactive':
                    Miners().schedule('start', self)
コード例 #14
0
ファイル: calibrate.py プロジェクト: rcespa/minotaur
    def start(self,
              device,
              pool_name,
              miner,
              algorithm,
              region,
              quick=False,
              force=False):
        self.miner = miner
        self.device = device
        self.default_profile = Config().get('device_profiles.default')

        Miners().reload_config()

        device.pin = {
            'pool_name': pool_name,
            'miner_name': miner.name,
            'algorithm': algorithm,
            'region': region
        }

        if not Config().get('calibration'):
            Log().add('fatal', "calibration config missing from config file")

        for key in [
                "hashrate_stabilisation_timeout_mins",
                "hashrate_stabilisation_tolerance",
                "hashrate_stabilisation_consecutive_readings_required",
                "algorithm_start_timeout"
        ]:
            if Config().get('calibration.%s' % (key)) == None:
                Log().add('fatal',
                          "missing config option: calibration.%s" % (key))

        if Config().get('calibration.power_tuning.enable'):
            for key in ["decrement_watts", "acceptable_loss_percent"]:
                if not Config().get('calibration.power_tuning.%s' % (key)):
                    Log().add(
                        'fatal',
                        "missing config option: calibration.power_tuning.%s" %
                        (key))

        Config().set('pools.%s.append_device_id_to_worker_name' % (pool_name),
                     False)

        if pool_name == 'miningpoolhub':
            if Config().get('pools.miningpoolhub.hub_workers.%s' %
                            (algorithm))[-5:] != 'CALIB':
                Config().set(
                    'pools.miningpoolhub.hub_workers.%s' % (algorithm),
                    Config().get('pools.miningpoolhub.hub_workers.%s' %
                                 (algorithm)) + 'CALIB')
        else:
            if Config().get('pools.%s.worker_name' %
                            (pool_name))[-5:] != 'CALIB':
                Config().set(
                    'pools.%s.worker_name' % (pool_name),
                    Config().get('pools.%s.worker_name' % (pool_name)) +
                    "CALIB")

        if quick:
            Config().set('calibration.power_tuning.enable', False)

        profile = Profiles().get_for_device_algo(device, algorithm)

        Log().add('info', '     device id: %d' % (device.id))
        Log().add('info', '          pool: %s' % (pool_name))
        Log().add('info', '  device class: %s' % (device.dclass))
        Log().add('info', 'device profile: %s' % (profile.name))
        Log().add('info', '         miner: %s' % (miner.name))
        Log().add('info', '     algorithm: %s' % (algorithm))
        Log().add('info', '        region: %s' % (region))

        Miners().poll()
        Miners().get_device_state(device)

        if device.state != 'inactive':
            if device.state == 'calibrating':
                device.log(
                    'fatal',
                    'this device is already being used for calibration')

            if force:
                device.log('info', 'terminating existing worker')
                if not device.stop(True):
                    device.log('fatal', 'failed to stop existing worker')
            else:
                device.log(
                    'fatal',
                    'unable to start calibration - device is in use (use --force to override)'
                )

        self.set_pin(device.id)

        default_power_limit = device.default_power_limit_f
        min_power_limit = device.min_power_limit_f
        max_power_limit = device.max_power_limit_f

        if max_power_limit == 0:
            device.log('info', 'device does not support power monitoring')
        else:
            device.log('info', 'max power limit: %d W' % (max_power_limit))

        device.log(
            'info', 'stabilisation tolerance: %.2f%%' %
            (Config().get('calibration.hashrate_stabilisation_tolerance')))

        if Config().get('debug'):
            device.log('debug', 'loading default profile')

        Nvidia().set_default_profile(device)

        if Config().get('debug'):
            device.log('debug', 'initialising pools')

        Pools()

        sample_timeout = Config().get(
            'calibration.hashrate_stabilisation_timeout_mins')

        if default_power_limit != None and Config().get(
                'calibration.power_tuning.enable'):
            device.log(
                'info',
                'starting initial run at max power limit [timeout=%dmins]' %
                (sample_timeout))
        else:
            device.log(
                'info',
                'starting single run [timeout=%dmins]' % (sample_timeout))

        Miners().poll()
        Miners().get_device_state(device)

        if device.state != 'calibrating':
            if not device.start(True):
                device.log('fatal', 'worker failed to start')

        if Config().get('calibration.power_tuning.enable'):
            if not device.power_supported():
                device.log('info', 'device does not support power monitoring')
            else:
                device.set_power_limit(max_power_limit)

        initial_hashrate, initial_max_power_draw = self.get_max_hashrate_and_power_draw(
            miner, device, sample_timeout * 60)

        if initial_hashrate == None:
            device.log(
                'info',
                'skipping algorithm as we failed to get a stable reading')
            print ""
            return

        if initial_max_power_draw != None and initial_max_power_draw > max_power_limit:
            initial_max_power_draw = max_power_limit

        device.log(
            'info', 'benchmark hashrate: %s' %
            (Units().hashrate_str(initial_hashrate)))

        if initial_max_power_draw != None:
            device.log('info',
                       'max power draw: %.2f' % (initial_max_power_draw))
            initial_power_limit = int(math.ceil(initial_max_power_draw))

        hashrate = initial_hashrate

        if initial_max_power_draw == None:
            power_limit = None
        elif Config().get('calibration.power_tuning.enable'):
            if max_power_limit == 0 or min_power_limit == 0 or default_power_limit == 0:
                device.log(
                    'error',
                    'device did not give us sane values for its min/max/default power limits'
                )
                device.log(
                    'error',
                    'unable to proceed with power calibration - device may not support changing the power limit'
                )

                power_limit = default_power_limit
            else:
                power_limit = initial_power_limit

                device.log('info', 'tuning power limit for optimum efficiency')
                hashrate, power_limit = self.do_power_calibration(
                    device, power_limit, miner, hashrate, min_power_limit)
        else:
            power_limit = default_power_limit

        Nvidia().set_default_profile(device)

        self.unset_pin(device.id)

        device.log('info', 'storing calibration data')

        Calibration().update_calibration_data(device.dclass, miner.name,
                                              algorithm, hashrate, power_limit)

        if not device.stop(True):
            device.log('fatal', "failed to stop the miner process")

        print ""

        device.log(
            'info',
            '   calibrated hashrate: %s' % (Units().hashrate_str(hashrate)))

        if power_limit != None:
            device.log('info',
                       'calibrated power limit: %.2f W' % (power_limit))

        print ""
コード例 #15
0
ファイル: calibrate.py プロジェクト: rcespa/minotaur
    def get_max_hashrate_and_power_draw(self, miner, device, time_limit):
        max_power_draw = 0

        timeout = 30
        count = 0

        while len(device.algos) == 0:
            time.sleep(1)
            Miners.poll()
            count += 1

            if count >= 30:
                device.log('fatal', 'timed out waiting for worker to start')

        algorithm = device.algos[0]['algo']

        if "_" in algorithm:
            readings_a = []
            readings_b = []
        else:
            readings = []

        time_start = time.time()
        count = 0
        err = 0
        reading_count = 0
        stable = False
        variance_pc = None

        while stable == False and (time.time() - time_start) <= time_limit:
            time.sleep(1)

            if count % 5 == 0:
                Miners().poll()

                if not Miners().is_up(miner.name):
                    device.log('fatal', 'miner API went away, aborting')

                Miners().get_device_state(device)

                if len(device.algos) == 0:
                    device.log('fatal',
                               'worker was stopped by an external process')

                rate_a, rate_b = device.algos[0]['hashrate']

                if "_" in algorithm:
                    r_len = len(readings_a)
                else:
                    r_len = len(readings)

                if r_len > 0:
                    if "_" in algorithm:
                        variance_pc, n = Calibration().get_variance(
                            readings_a + [rate_a], readings_b + [rate_b])
                    else:
                        variance_pc, n = Calibration().get_variance(readings +
                                                                    [rate_a])

                    device.log(
                        'info', 'hashrate: %s (variance %.2f%%)' %
                        (device.hashrate_str(), variance_pc))
                else:
                    device.log('info',
                               'hashrate: %s' % (device.hashrate_str()))

                if rate_a == 0:
                    if reading_count >= 3:
                        device.log('warning', 'hashrate dropped to 0!')

                        if device.state != 'calibrating':
                            device.log(
                                'fatal',
                                'our worker was stopped by an external process, aborting.'
                            )

                        err += 1

                        if err >= 10:
                            device.stop()
                            device.log(
                                'fatal',
                                'failed to get a hashrate reading from the worker'
                            )
                else:
                    if "_" in algorithm:
                        readings_a.append(rate_a)
                        readings_b.append(rate_b)
                    else:
                        readings.append(rate_a)

                    if variance_pc != None and Calibration().is_stable(
                            variance_pc, n):
                        device.log('info', 'hashrate stabilised')
                        stable = True

                reading_count += 1

            count += 1

            metrics = Nvidia().get_nvidia_metrics_for_device(device.id)

            if metrics['power_f']:
                max_power_draw = metrics['power_f']
            else:
                max_power_draw = None

        if stable == False:
            device.stop(True)

            device.log(
                'error',
                'hashrate failed to stabilise after %d minutes\ntry increasing the timeout or loosen the stabilisation tolerance by increasing the "hashrate_stabilisation_tolerance" parameter'
                % (time_limit / 60))

            return [None, None]

        if "_" in algorithm:
            hashrate = [
                Calibration().get_nominal_hashrate_from_range(readings_a),
                Calibration().get_nominal_hashrate_from_range(readings_b)
            ]
        else:
            hashrate = Calibration().get_nominal_hashrate_from_range(readings)

        return [hashrate, max_power_draw]
コード例 #16
0
    def get_local_data(self, minimal=False):
        data = []

        devices = nvidia.Nvidia().get_nvidia_devices(1, True)

        Miners().poll()

        if not minimal and os.path.exists(
                self.power_file) and os.path.getsize(self.power_file) > 0:
            power_draw_readings = pickle.loads(open(self.power_file).read())
        else:
            power_draw_readings = []

        if not minimal and os.path.exists(
                self.profitability_file) and os.path.getsize(
                    self.profitability_file) > 0:
            profitability_readings = pickle.loads(
                open(self.profitability_file).read())
        else:
            profitability_readings = []

        Calibration().load()

        total_mbtc = 0
        total_power = 0
        total_power_limit = 0

        power_values = {}
        power_limit_values = {}

        for device in devices:
            device.algos = []
            Miners().get_device_state(device)

            mbtc_per_day_values = [0]

            algos = device.algos

            if len(algos) == 0:
                algos = ['IDLE']

            for algo_i in range(0, len(algos)):
                algo = algos[algo_i]

                if algo_i == 0:
                    omit_fields = False
                else:
                    omit_fields = True

                if algo == 'IDLE':
                    algo = "IDLE"
                    algo1 = "IDLE"
                    rate_s = "-"
                    mbtc_per_day = 0
                    miner = '-'
                    region = '-'
                    pool = '-'
                    _time = '-'
                else:
                    algo1 = algo['algo']
                    miner = algo['miner']
                    region = algo['region']
                    pool = algo['pool']
                    _pool = Pools().pools[pool]

                    if os.path.exists("/tmp/.minotaur.%d" % (device.id)):
                        _time = Units().to_timestr(
                            int(time.time() - os.stat("/tmp/.minotaur.%d" %
                                                      (device.id)).st_mtime))
                    else:
                        _time = '-'

                    rate_a, rate_b = algo['hashrate']

                    if algo['algo'] in Config().get('algorithms')['double']:
                        rate_s = Units().hashrate_str(rate_a)
                        rate_s2 = Units().hashrate_str(rate_b)

                        benchmarks = {algo['algo']: [rate_a, rate_b]}

                        algo1, algo2 = algo['algo'].split("_")

                        mbtc_per_day_values = [
                            _pool.mbtc_per_day(
                                benchmarks,
                                'cached')[algo['region']][algo['algo']]
                        ]
                    else:
                        rate_s = Units().hashrate_str(rate_a)

                        benchmarks = {algo['algo']: rate_a}

                        if algo['algo'] in _pool.mbtc_per_day(
                                benchmarks, 'cached')[algo['region']].keys():
                            mbtc_per_day_values = [
                                _pool.mbtc_per_day(
                                    benchmarks,
                                    'cached')[algo['region']][algo['algo']]
                            ]
                        else:
                            mbtc_per_day_values = [0]

                if pool != '-':
                    pool = _pool.shortened(region)

                metrics = device.to_hash()
                metrics.pop('changed', None)
                metrics['miner'] = miner
                metrics['region'] = region
                metrics['pool'] = pool
                metrics['time'] = _time
                metrics['omit_fields'] = omit_fields

                if metrics['fan']:
                    metrics['fan'] = str(metrics['fan']).rjust(3) + " %"

                if metrics['miner']:
                    metrics['miner'] = metrics['miner'][0:5]
                else:
                    metrics['miner'] = '-'

                if algo == "IDLE":
                    if metrics['gpu_u_i'] and metrics['gpu_u_i'] >= 90:
                        algo = ".pending."
                        algo1 = ".pending."
                        rate_s = ".."

                mbtc_per_day = mbtc_per_day_values[0]

                metrics['host'] = 'local'
                metrics['id'] = device.id
                metrics[" algo"] = algo1
                metrics["rate"] = rate_s

                metrics[" mBTC/day"] = ("%.2f" % (mbtc_per_day)).rjust(5)

                if not metrics['region']:
                    metrics['region'] = '-'

                total_mbtc += sum(mbtc_per_day_values)

                if metrics['power_f']:
                    power_values[metrics['id']] = metrics['power_f']
                    margin = self.calculate_profit_margin_for_card(
                        sum(mbtc_per_day_values), metrics['power_f'])

                    net_mbtc = (mbtc_per_day / 100) * margin

                    if margin < 0:
                        margin = 0

                    margin_s = "%d%%" % (int(margin))

                    metrics[" mBTC/day"] += "/"
                    metrics[" mBTC/day"] += ("%.2f" % (net_mbtc)).rjust(5)
                    metrics[" mBTC/day"] += " %s" % (margin_s.rjust(4))
                else:
                    margin = 0

                if margin > 0:
                    metrics["margin"] = "%.1f%%" % (margin)
                else:
                    metrics["margin"] = "-"

                if metrics['limit_f']:
                    power_limit_values[metrics['id']] = metrics['limit_f']

                if device.state == 'calibrating':
                    metrics[' algo'] = '*' + metrics[' algo']
                elif device.get_pin() and 'algorithm' in device.get_pin().keys(
                ) and device.get_pin()['algorithm'] == metrics[' algo']:
                    metrics[' algo'] = '+' + metrics[' algo']
                elif device.get_pin() and 'algorithm' in device.get_pin().keys(
                ) and '_' in device.get_pin()['algorithm'] and metrics[
                        ' algo'] in device.get_pin()['algorithm'].split('_'):
                    metrics[' algo'] = '+' + metrics[' algo']
                else:
                    metrics[' algo'] = ' ' + metrics[' algo']

                if metrics['gpu_f']:
                    match = re.match("^([\d]+)", metrics['gpu_f'])
                    if match:
                        metrics['gpu_f'] = match.group(1)
                    else:
                        metrics['gpu_f'] = '-'
                else:
                    metrics['gpu_f'] = '-'

                if metrics['mem_f']:
                    match = re.match("^([\d]+)", metrics['mem_f'])
                    if match:
                        metrics['mem_f'] = match.group(1)
                    else:
                        metrics['mem_f'] = '-'
                else:
                    metrics['mem_f'] = '-'

                metrics['ps clocks'] = "%s %s/%s MHz" % (
                    metrics['ps'], metrics['gpu_f'], metrics['mem_f'])

                power = re.match("^([\d]+)", metrics['power'])
                limit = re.match("^([\d]+)", metrics['limit'])

                if power and limit:
                    metrics['power'] = "%s/%s W" % (power.group(1),
                                                    limit.group(1))
                else:
                    metrics['power'] = '-'

                data.append(metrics)

                if algo not in [
                        'IDLE', '.pending.'
                ] and algo['algo'] in Config().get('algorithms.double'):
                    mbtc_per_day = mbtc_per_day_values[1]

                    metrics2 = metrics.copy()
                    metrics2[" algo"] = algo2

                    metrics2["rate"] = rate_s2

                    metrics2[" mBTC/day"] = ("%.2f" % (mbtc_per_day)).rjust(5)

                    net_mbtc = (mbtc_per_day / 100) * margin

                    metrics2[" mBTC/day"] += "/"
                    metrics2[" mBTC/day"] += ("%.2f" % (net_mbtc)).rjust(5)
                    metrics2[" mBTC/day"] += " %s" % (margin_s.rjust(4))

                    if device.state == 'calibrating':
                        metrics2[" algo"] = '*' + metrics2[' algo']
                    elif device.get_pin() and '_' in device.get_pin(
                    )['algorithm'] and metrics2[' algo'] in device.get_pin(
                    )['algorithm'].split('_'):
                        metrics2[" algo"] = '+' + metrics2[' algo']
                    else:
                        metrics2[" algo"] = ' ' + metrics2[' algo']

                    data.append(metrics2)

        total_power = sum(power_values.values())
        total_power_limit = sum(power_limit_values.values())

        electric_cost_mbtc = self.get_electricity_cost(
            total_power + Config().get('system_draw_watts'), True,
            Config().get('electricity_per_kwh'))
        net_mbtc = total_mbtc - electric_cost_mbtc

        return [
            data, total_mbtc, net_mbtc, total_power, total_power_limit,
            power_draw_readings, profitability_readings
        ]