diff --git a/src/scan.c b/src/scan.c
index aeab6516..2787028d 100644
--- a/src/scan.c
+++ b/src/scan.c
@@ -51,6 +51,7 @@
 #include "src/mpdu.h"
 #include "src/band.h"
 #include "src/scan.h"
+#include "src/blacklist.h"
 
 /* User configurable options */
 static double RANK_2G_FACTOR;
@@ -58,6 +59,7 @@ static double RANK_5G_FACTOR;
 static double RANK_6G_FACTOR;
 static uint32_t RANK_HIGH_UTILIZATION;
 static uint32_t RANK_HIGH_STATION_COUNT;
+static int RANK_OPTIMAL_SIGNAL_THRESHOLD;
 static uint32_t SCAN_MAX_INTERVAL;
 static uint32_t SCAN_INIT_INTERVAL;
 
@@ -1910,15 +1912,41 @@ int scan_bss_get_security(const struct scan_bss *bss, enum security *security)
 	return 0;
 }
 
+/*
+ * Evaluate the BSS's grouping based on its signal strength and blacklist
+ * status. From best to worst the groupings are:
+ *
+ * Optimal: Not blacklisted in any form, and above the RSSI threshold
+ * Above Threshold: Above the RSSI threshold (may be roam blacklisted)
+ * Below Threshold: Below the RSSI threshold (may be roam blacklisted)
+ * Blacklisted: Permanently blacklisted
+ */
+enum scan_bss_group scan_bss_evaluate_group(const uint8_t *addr,
+						int16_t signal_strength)
+{
+	int rssi = signal_strength / 100;
+
+	if (RANK_OPTIMAL_SIGNAL_THRESHOLD == 0)
+		return SCAN_BSS_GROUP_OPTIMAL;
+
+	if (blacklist_contains_bss(addr, BLACKLIST_REASON_PERMANENT))
+		return SCAN_BSS_GROUP_BLACKLISTED;
+
+	if (!blacklist_contains_bss(addr, BLACKLIST_REASON_ROAM_REQUESTED) &&
+			rssi >= RANK_OPTIMAL_SIGNAL_THRESHOLD)
+		return SCAN_BSS_GROUP_OPTIMAL;
+
+	if (rssi >= RANK_OPTIMAL_SIGNAL_THRESHOLD)
+		return SCAN_BSS_GROUP_ABOVE_THRESHOLD;
+
+	return SCAN_BSS_GROUP_UNDER_THRESHOLD;
+}
+
 int scan_bss_rank_compare(const void *a, const void *b, void *user_data)
 {
 	const struct scan_bss *new_bss = a, *bss = b;
 
-	if (bss->rank == new_bss->rank)
-		return (bss->signal_strength >
-					new_bss->signal_strength) ? 1 : -1;
-
-	return (bss->rank > new_bss->rank) ? 1 : -1;
+	return __scan_bss_rank_compare(new_bss, bss);
 }
 
 static bool scan_survey_get_snr(struct scan_results *results,
@@ -2678,6 +2706,10 @@ static int scan_init(void)
 	if (L_WARN_ON(RANK_HIGH_STATION_COUNT > 255))
 		RANK_HIGH_STATION_COUNT = 255;
 
+	if (!l_settings_get_int(config, "Rank", "OptimalSignalThreshold",
+					&RANK_OPTIMAL_SIGNAL_THRESHOLD))
+		RANK_OPTIMAL_SIGNAL_THRESHOLD = 0;
+
 	return 0;
 }
 
diff --git a/src/scan.h b/src/scan.h
index 4c1ebc21..4d06f29d 100644
--- a/src/scan.h
+++ b/src/scan.h
@@ -45,6 +45,17 @@ enum scan_bss_frame_type {
 	SCAN_BSS_BEACON,
 };
 
+/*
+ * Groupings for BSS's. These are assumed to be in order of preference where
+ * the last enum is the most preferred group to connect to.
+ */
+enum scan_bss_group {
+	SCAN_BSS_GROUP_BLACKLISTED,
+	SCAN_BSS_GROUP_UNDER_THRESHOLD,
+	SCAN_BSS_GROUP_ABOVE_THRESHOLD,
+	SCAN_BSS_GROUP_OPTIMAL,
+};
+
 struct scan_bss {
 	uint8_t addr[6];
 	uint32_t frequency;
@@ -168,6 +179,36 @@ bool scan_get_firmware_scan(uint64_t wdev_id, scan_notify_func_t notify,
 				void *userdata, scan_destroy_func_t destroy);
 
 void scan_bss_free(struct scan_bss *bss);
+
+enum scan_bss_group scan_bss_evaluate_group(const uint8_t *addr,
+						int16_t signal_strength);
+
+/*
+ * Macro to compare two scan_bss-like objects. This is to share code between
+ * station.c and scan.c since station uses "roam_bss" objects which is a subset
+ * of a scan_bss.
+ *  - If the groups differ this is used as the comparison.
+ *  - If the groups match, the BSS rank is used as the comparison
+ *  - If the BSS ranks match, the signal strength is used as the comparison
+ */
+#define __scan_bss_rank_compare(a, b) ({			\
+	int ret;						\
+	enum scan_bss_group a_group = scan_bss_evaluate_group(	\
+			(a)->addr, (a)->signal_strength);	\
+	enum scan_bss_group b_group = scan_bss_evaluate_group(	\
+			(b)->addr, (b)->signal_strength);	\
+	if (b_group > a_group)					\
+		ret = 1;					\
+	else if (b_group < a_group)				\
+		ret = -1;					\
+	else if ((b)->rank == (a)->rank)			\
+		ret = ((b)->signal_strength > 			\
+				(a)->signal_strength) ? 1 : -1;	\
+	else							\
+		ret = ((b)->rank > (a)->rank) ? 1 : -1;		\
+	ret;							\
+})
+
 int scan_bss_rank_compare(const void *a, const void *b, void *user);
 
 int scan_bss_get_rsn_info(const struct scan_bss *bss, struct ie_rsn_info *info);
