[gnome-disk-utility/ata-smart-ui-rework] Land new ATA SMART user interface



commit 9d910f0c70e85e4efd0cb6c26f4ee98997c89c1b
Author: David Zeuthen <davidz redhat com>
Date:   Sun Aug 23 14:10:51 2009 -0400

    Land new ATA SMART user interface
    
    For now we don't have any graphs. It remains to be decided if we want
    to add them back.

 configure.ac                            |    4 +
 src/gdu-gtk/Makefile.am                 |   14 +-
 src/gdu-gtk/gdu-ata-smart-dialog.c      | 3116 +++++++++++++++++++++++--------
 src/gdu-gtk/gdu-ata-smart-dialog.h      |    2 +-
 src/gdu-gtk/gdu-curve.c                 |  499 -----
 src/gdu-gtk/gdu-curve.h                 |  110 --
 src/gdu-gtk/gdu-graph.c                 | 1312 -------------
 src/gdu-gtk/gdu-graph.h                 |   75 -
 src/gdu-gtk/gdu-gtk-enums.h             |   24 -
 src/gdu-gtk/gdu-gtk-enumtypes.h         |    4 -
 src/gdu-gtk/gdu-gtk-types.h             |    1 +
 src/gdu-gtk/gdu-gtk.h                   |    3 +-
 src/gdu-gtk/gdu-pool-tree-view.c        |    2 +-
 src/gdu/Makefile.am                     |    4 -
 src/gdu/gdu-ata-smart-attribute.c       |  622 ------
 src/gdu/gdu-ata-smart-attribute.h       |   86 -
 src/gdu/gdu-ata-smart-historical-data.c |  181 --
 src/gdu/gdu-ata-smart-historical-data.h |   72 -
 src/gdu/gdu-callbacks.h                 |    5 -
 src/gdu/gdu-device.c                    |  299 +---
 src/gdu/gdu-device.h                    |   64 +-
 src/gdu/gdu-private.h                   |    4 -
 src/gdu/gdu-types.h                     |    2 -
 src/gdu/gdu-util.c                      |   91 +-
 src/gdu/gdu-util.h                      |    5 +
 src/gdu/gdu.h                           |    2 -
 src/notification/notification-main.c    |  157 ++-
 src/palimpsest/Makefile.am              |    1 -
 src/palimpsest/gdu-section-health.c     | 1683 -----------------
 src/palimpsest/gdu-section-health.h     |   58 -
 src/palimpsest/gdu-shell.c              |   96 +-
 31 files changed, 2707 insertions(+), 5891 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1cce5ad..e22fd74 100644
--- a/configure.ac
+++ b/configure.ac
@@ -145,6 +145,10 @@ PKG_CHECK_MODULES(UNIQUE, unique-1.0 >= $UNIQUE_REQUIRED)
 PKG_CHECK_MODULES(LIBNOTIFY, libnotify  >= $LIBNOTIFY_REQUIRED)
 PKG_CHECK_MODULES(DEVKIT_DISKS, DeviceKit-disks  >= $DEVKIT_DISKS_REQUIRED)
 
+PKG_CHECK_MODULES(LIBATASMART, [libatasmart >= 0.14])
+AC_SUBST(LIBATASMART_CFLAGS)
+AC_SUBST(LIBATASMART_LIBS)
+
 # *************
 # Documentation
 # *************
diff --git a/src/gdu-gtk/Makefile.am b/src/gdu-gtk/Makefile.am
index 419fa63..3eba71e 100644
--- a/src/gdu-gtk/Makefile.am
+++ b/src/gdu-gtk/Makefile.am
@@ -36,8 +36,7 @@ libgdu_gtkinclude_HEADERS =              				\
 	gdu-size-widget.h						\
 	gdu-create-linux-md-dialog.h					\
 	gdu-ata-smart-dialog.h						\
-	gdu-curve.h							\
-	gdu-graph.h							\
+	gdu-spinner.h							\
 	$(NULL)
 
 libgdu_gtk_la_SOURCES =                 	               				\
@@ -51,8 +50,7 @@ libgdu_gtk_la_SOURCES =                 	               				\
 	gdu-size-widget.h			gdu-size-widget.c			\
 	gdu-create-linux-md-dialog.h		gdu-create-linux-md-dialog.c		\
 	gdu-ata-smart-dialog.h			gdu-ata-smart-dialog.c			\
-	gdu-curve.h				gdu-curve.c				\
-	gdu-graph.h				gdu-graph.c				\
+	gdu-spinner.h				gdu-spinner.c				\
 	$(NULL)
 
 libgdu_gtk_la_CPPFLAGS = 				\
@@ -75,7 +73,9 @@ libgdu_gtk_la_CFLAGS = 					\
 	$(GNOME_KEYRING_CFLAGS)				\
 	$(GTK2_CFLAGS)					\
 	$(WARN_CFLAGS)					\
-	$(AM_CFLAGS)
+	$(AM_CFLAGS)					\
+	$(LIBATASMART_CFLAGS)				\
+	$(NULL)
 
 libgdu_gtk_la_LIBADD = 					\
 	$(GLIB2_LIBS)					\
@@ -84,7 +84,9 @@ libgdu_gtk_la_LIBADD = 					\
 	$(DBUS_GLIB_LIBS)				\
 	$(GNOME_KEYRING_LIBS)				\
 	$(GTK2_LIBS)					\
-	$(INTLLIBS)
+	$(INTLLIBS)					\
+	$(LIBATASMART_LIBS)				\
+	$(NULL)
 
 libgdu_gtk_la_LDFLAGS = -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
 		        -export-dynamic -no-undefined -export-symbols-regex '(^gdu_.*)'
diff --git a/src/gdu-gtk/gdu-ata-smart-dialog.c b/src/gdu-gtk/gdu-ata-smart-dialog.c
index 809c536..164faa2 100644
--- a/src/gdu-gtk/gdu-ata-smart-dialog.c
+++ b/src/gdu-gtk/gdu-ata-smart-dialog.c
@@ -21,82 +21,637 @@
 
 #include <config.h>
 #include <glib/gi18n.h>
+#include <atasmart.h>
+#include <glib/gstdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
 
 #include "gdu-time-label.h"
-#include "gdu-curve.h"
-#include "gdu-graph.h"
 #include "gdu-ata-smart-dialog.h"
+#include "gdu-spinner.h"
+#include "gdu-pool-tree-model.h"
+
+/* ---------------------------------------------------------------------------------------------------- */
 
 struct GduAtaSmartDialogPrivate
 {
+        GduDrive *drive;
         GduDevice *device;
 
         guint64 last_updated;
         gulong device_changed_signal_handler_id;
+        gulong device_job_changed_signal_handler_id;
+
+        GduPoolTreeModel *pool_tree_model;
+        GtkWidget *drive_combo_box;
 
+        GtkWidget *updated_label;
+        GtkWidget *updating_spinner;
+        GtkWidget *updating_label;
+        GtkWidget *update_link_label;
+
+        GtkWidget *self_test_result_label;
+        GtkWidget *self_test_progress_bar;
+        GtkWidget *self_test_run_link_label;
+        GtkWidget *self_test_cancel_link_label;
+
+        GtkWidget *model_label;
+        GtkWidget *firmware_label;
+        GtkWidget *serial_label;
         GtkWidget *power_on_hours_label;
         GtkWidget *temperature_label;
-        GtkWidget *last_self_test_result_label;
-        GtkWidget *updated_label;
-        GtkWidget *assessment_label;
         GtkWidget *sectors_label;
-        GtkWidget *attributes_label;
+        GtkWidget *self_assessment_label;
+        GtkWidget *overall_assessment_image;
+        GtkWidget *overall_assessment_label;
+        GtkWidget *no_warn_check_button;
 
         GtkWidget *tree_view;
         GtkListStore *attr_list_store;
 
-        GtkWidget *graph;
+        gboolean is_updating;
 
-        GList *historical_data;
+        gboolean has_been_constructed;
 };
 
 enum
 {
         PROP_0,
-        PROP_DEVICE,
+        PROP_DRIVE,
 };
 
+/* ---------------------------------------------------------------------------------------------------- */
 
-enum
+#define _GDU_TYPE_SK_ATTR (_gdu_type_sk_attr_get_type ())
+
+static SkSmartAttributeParsedData *
+_gdu_sk_attr_copy (const SkSmartAttributeParsedData *instance)
+{
+        SkSmartAttributeParsedData *ret;
+
+        ret = g_new0 (SkSmartAttributeParsedData, 1);
+        memcpy (ret, instance, sizeof (SkSmartAttributeParsedData));
+        ret->name = g_strdup (instance->name);
+
+        return ret;
+}
+
+static void
+_gdu_sk_attr_free (SkSmartAttributeParsedData *instance)
+{
+        g_free ((gchar *) instance->name);
+        g_free (instance);
+}
+
+static GType
+_gdu_type_sk_attr_get_type (void)
 {
-        ATTR_NAME_COLUMN,
-        ATTR_ID_INT_COLUMN,
-        ATTR_ID_COLUMN,
-        ATTR_DESC_COLUMN,
-        ATTR_CURRENT_COLUMN,
-        ATTR_WORST_COLUMN,
-        ATTR_THRESHOLD_COLUMN,
-        ATTR_VALUE_COLUMN,
-        ATTR_STATUS_PIXBUF_COLUMN,
-        ATTR_STATUS_TEXT_COLUMN,
-        ATTR_TYPE_COLUMN,
-        ATTR_UPDATES_COLUMN,
-        ATTR_TOOLTIP_COLUMN,
-        ATTR_N_COLUMNS,
+        static volatile gsize type_volatile = 0;
+
+        if (g_once_init_enter (&type_volatile)) {
+                GType type = g_boxed_type_register_static (
+                                                           g_intern_static_string ("_GduSkAttr"),
+                                                           (GBoxedCopyFunc) _gdu_sk_attr_copy,
+                                                           (GBoxedFreeFunc) _gdu_sk_attr_free);
+                g_once_init_leave (&type_volatile, type);
+        }
+
+        return type_volatile;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct {
+        const gchar *name;
+        const gchar *pretty_name;
+        const gchar *desc;
+} SmartDetails;
+
+/* See http://smartmontools.sourceforge.net/doc.html
+ *     http://en.wikipedia.org/wiki/S.M.A.R.T
+ *     http://www.t13.org/Documents/UploadedDocuments/docs2005/e05148r0-ACS-ATA_SMARTAttributesAnnex.pdf
+ *
+ *     Keep in sync with libatasmart. Last sync: Thu Aug 20 2009
+ */
+static const SmartDetails smart_details[] = {
+        {
+                "raw-read-error-rate",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Read Error Rate"),
+                N_("Frequency of errors while reading raw data from the disk. "
+                   "A non-zero value indicates a problem with "
+                   "either the disk surface or read/write heads")
+        },
+        {
+                "throughput-performance",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Throughput Performance"),
+                N_("Average efficiency of the disk")
+        },
+        {
+                "spin-up-time",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Spinup Time"),
+                N_("Time needed to spin up the disk")
+        },
+        {
+                "start-stop-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Start/Stop Count"),
+                N_("Number of spindle start/stop cycles")
+        },
+        {
+                "reallocated-sector-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Reallocated Sector Count"),
+                N_("Count of remapped sectors. "
+                   "When the hard drive finds a read/write/verification error, it mark the sector "
+                   "as \"reallocated\" and transfers data to a special reserved area (spare area)")
+        },
+        {
+                "read-channel-margin",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Read Channel Margin"),
+                N_("Margin of a channel while reading data.")
+        },
+        {
+                "seek-error-rate",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Seek Error Rate"),
+                N_("Frequency of errors while positioning")
+        },
+        {
+                "seek-time-performance",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Seek Timer Performance"),
+                N_("Average efficiency of operatings while positioning")
+        },
+        {
+                "power-on-hours",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Power-On Hours"),
+                N_("Number of hours elapsed in the power-on state")
+        },
+        {
+                "spin-retry-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Spinup Retry Count"),
+                N_("Number of retry attempts to spin up")
+        },
+        {
+                "calibration-retry-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Calibration Retry Count"),
+                N_("Number of attempts to calibrate the device")
+        },
+        {
+                "power-cycle-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Power Cycle Count"),
+                N_("Number of power-on events")
+        },
+        {
+                "read-soft-error-rate",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Soft read error rate"),
+                N_("Frequency of 'program' errors while reading from the disk")
+        },
+        {
+                "reported-uncorrect",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Reported Uncorrectable Errors"),
+                N_("Number of errors that could not be recovered using hardware ECC")
+        },
+        {
+                "high-fly-writes",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("High Fly Writes"),
+                N_("Number of times a recording head is flying outside its normal operating range")
+        },
+        {
+                "airflow-temperature-celsius",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Airflow Temperature"),
+                N_("Airflow temperature of the drive")
+        },
+        {
+                "g-sense-error-rate",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("G-sense Error Rate"),
+                N_("Frequency of mistakes as a result of impact loads")
+        },
+        {
+                "power-off-retract-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Power-off Retract Count"),
+                N_("Number of power-off or emergency retract cycles")
+        },
+        {
+                "load-cycle-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Load/Unload Cycle Count"),
+                N_("Number of cycles into landing zone position")
+        },
+        {
+                "temperature-celsius-2",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Temperature"),
+                N_("Current internal temperature of the drive")
+        },
+        {
+                "hardware-ecc-recovered",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Hardware ECC Recovered"),
+                N_("Number of ECC on-the-fly errors")
+        },
+        {
+                "reallocated-event-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Reallocation Count"),
+                N_("Number of remapping operations. "
+                   "The raw value of this attribute shows the total number of (successful "
+                   "and unsuccessful) attempts to transfer data from reallocated sectors "
+                   "to a spare area")
+        },
+        {
+                "current-pending-sector",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Current Pending Sector Count"),
+                N_("Number of sectors waiting to be remapped. "
+                   "If the sector waiting to be remapped is subsequently written or read "
+                   "successfully, this value is decreased and the sector is not remapped. Read "
+                   "errors on the sector will not remap the sector, it will only be remapped on "
+                   "a failed write attempt")
+        },
+        {
+                "offline-uncorrectable",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Uncorrectable Sector Count"),
+                N_("The total number of uncorrectable errors when reading/writing a sector. "
+                   "A rise in the value of this attribute indicates defects of the "
+                   "disk surface and/or problems in the mechanical subsystem")
+        },
+        {
+                "udma-crc-error-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("UDMA CRC Error Rate"),
+                N_("Number of CRC errors during UDMA mode")
+        },
+        {
+                "multi-zone-error-rate",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Write Error Rate"),
+                N_("Number of errors while writing to disk (or) multi-zone error rate (or) flying-height")
+        },
+        {
+                "soft-read-error-rate",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Soft Read Error Rate"),
+                N_("Number of off-track errors")
+        },
+        {
+                "ta-increase-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Data Address Mark Errors"),
+                N_("Number of Data Address Mark (DAM) errors (or) vendor-specific")
+        },
+        {
+                "run-out-cancel",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Run Out Cancel"),
+                N_("Number of ECC errors")
+        },
+        {
+                "shock-count-write-open",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Soft ECC correction"),
+                N_("Number of errors corrected by software ECC")
+        },
+        {
+                "shock-rate-write-open",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Thermal Asperity Rate"),
+                N_("Number of Thermal Asperity Rate errors")
+        },
+        {
+                "flying-height",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Flying Height"),
+                N_("Height of heads above the disk surface")
+        },
+        {
+                "spin-high-current",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Spin High Current"),
+                N_("Amount of high current used to spin up the drive")
+        },
+        {
+                "spin-buzz",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Spin Buzz"),
+                N_("Number of buzz routines to spin up the drive")
+        },
+        {
+                "offline-seek-performance",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Offline Seek Performance"),
+                N_("Drive's seek performance during offline operations")
+        },
+        {
+                "disk-shift",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Disk Shift"),
+                N_("Shift of disk is possible as a result of strong shock loading in the store, "
+                   "as a result of falling (or) temperature")
+        },
+        {
+                "g-sense-error-rate-2",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("G-sense Error Rate"),
+                N_("Number of errors as a result of impact loads as detected by a shock sensor")
+        },
+        {
+                "loaded-hours",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Loaded Hours"),
+                N_("Number of hours in general operational state")
+        },
+        {
+                "load-retry-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Load/Unload Retry Count"),
+                N_("Loading on drive caused by numerous recurrences of operations, like reading, "
+                   "recording, positioning of heads, etc")
+        },
+        {
+                "load-friction",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Load Friction"),
+                N_("Load on drive cause by friction in mechanical parts of the store")
+        },
+        {
+                "load-cycle-count-2",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Load/Unload Cycle Count"),
+                N_("Total number of load cycles")
+        },
+        {
+                "load-in-time",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Load-in Time"),
+                N_("General time for loading in a drive")
+        },
+        {
+                "torq-amp-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Torque Amplification Count"),
+                N_("Quantity efforts of the rotating moment of a drive")
+        },
+        {
+                "power-off-retract-count-2",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Power-off Retract Count"),
+                N_("Number of power-off retract events")
+        },
+        {
+                "head-amplitude",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("GMR Head Amplitude"),
+                N_("Amplitude of heads trembling (GMR-head) in running mode")
+        },
+        {
+                "temperature-celsius",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Temperature"),
+                N_("Temperature of the drive")
+        },
+        {
+                "endurance-remaining",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Endurance Remaining"),
+                N_("Number of physical erase cycles completed on the drive as "
+                   "a percentage of the maximum physical erase cycles the drive supports")
+        },
+        {
+                "power-on-seconds-2",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Power-On Hours"),
+                N_("Number of hours elapsed in the power-on state")
+        },
+        {
+                "uncorrectable-ecc-count",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Uncorrectable ECC Count"),
+                N_("Number of uncorrectable ECC errors")
+        },
+        {
+                "good-block-rate",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Good Block Rate"),
+                N_("Number of available reserved blocks as a percentage "
+                   "of the total number of reserved blocks"),
+        },
+        {
+                "head-flying-hours",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Head Flying Hours"),
+                N_("Time while head is positioning")
+        },
+        {
+                "read-error-retry-rate",
+                /* Translators: S.M.A.R.T attribute, see http://smartmontools.sourceforge.net/doc.html
+                 * or the next string for a longer explanation.
+                 */
+                N_("Read Error Retry Rate"),
+                N_("Number of errors while reading from a disk")
+        },
+        {
+                NULL,
+                NULL,
+                NULL
+        }
+};
+
+
+static void
+attribute_get_details (SkSmartAttributeParsedData  *a,
+                       gchar                      **out_name,
+                       gchar                      **out_description)
+{
+        const gchar *n;
+        const gchar *d;
+        SmartDetails *details;
+        static volatile gsize have_hash = 0;
+        static GHashTable *smart_details_map = NULL;
+
+        if (g_once_init_enter (&have_hash)) {
+                guint n;
+
+                smart_details_map = g_hash_table_new (g_str_hash, g_str_equal);
+                for (n = 0; smart_details[n].name != NULL; n++) {
+                        g_hash_table_insert (smart_details_map,
+                                             (gpointer) smart_details[n].name,
+                                             (gpointer) &(smart_details[n]));
+                }
+                g_once_init_leave (&have_hash, 1);
+        }
+
+        n = NULL;
+        d = NULL;
+        details = g_hash_table_lookup (smart_details_map, a->name);
+        if (details != NULL) {
+                n = _(details->pretty_name);
+                d = _(details->desc);
+        }
+
+        if (out_name != NULL)
+                *out_name = g_strdup (n);
+        if (out_description != NULL)
+                *out_description = g_strdup (d);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+enum {
+        ID_COLUMN,
+        NAME_COLUMN,
+        VALUE_COLUMN,
+        TOOLTIP_COLUMN,
+        SK_ATTR_COLUMN,
+        N_COLUMNS,
 };
 
 G_DEFINE_TYPE (GduAtaSmartDialog, gdu_ata_smart_dialog, GTK_TYPE_DIALOG)
 
 static void update_dialog (GduAtaSmartDialog *dialog);
 static void device_changed (GduDevice *device, gpointer user_data);
+static void device_job_changed (GduDevice *device, gpointer user_data);
 
 static gchar *pretty_to_string (guint64                  pretty_value,
-                                GduAtaSmartAttributeUnit pretty_unit,
-                                gboolean                 long_string);
+                                SkSmartAttributeUnit     pretty_unit);
+
+static gboolean get_ata_smart_no_warn (GduDevice *device);
+static gboolean set_ata_smart_no_warn (GduDevice *device,
+                                       gboolean   no_warn);
 
 static void
 gdu_ata_smart_dialog_finalize (GObject *object)
 {
         GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (object);
 
-        g_signal_handler_disconnect (dialog->priv->device, dialog->priv->device_changed_signal_handler_id);
-        g_object_unref (dialog->priv->device);
-        g_object_unref (dialog->priv->attr_list_store);
-
-        if (dialog->priv->historical_data != NULL) {
-                g_list_foreach (dialog->priv->historical_data, (GFunc) g_object_unref, NULL);
-                g_list_free (dialog->priv->historical_data);
+        if (dialog->priv->drive != NULL) {
+                g_object_unref (dialog->priv->drive);
         }
+        if (dialog->priv->device != NULL) {
+                g_signal_handler_disconnect (dialog->priv->device, dialog->priv->device_changed_signal_handler_id);
+                g_signal_handler_disconnect (dialog->priv->device, dialog->priv->device_job_changed_signal_handler_id);
+                g_object_unref (dialog->priv->device);
+        }
+        g_object_unref (dialog->priv->attr_list_store);
 
         if (G_OBJECT_CLASS (gdu_ata_smart_dialog_parent_class)->finalize != NULL)
                 G_OBJECT_CLASS (gdu_ata_smart_dialog_parent_class)->finalize (object);
@@ -111,8 +666,8 @@ gdu_ata_smart_dialog_get_property (GObject    *object,
         GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (object);
 
         switch (property_id) {
-        case PROP_DEVICE:
-                g_value_set_object (value, dialog->priv->device);
+        case PROP_DRIVE:
+                g_value_set_object (value, dialog->priv->drive);
                 break;
 
         default:
@@ -129,8 +684,35 @@ gdu_ata_smart_dialog_set_property (GObject      *object,
         GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (object);
 
         switch (property_id) {
-        case PROP_DEVICE:
-                dialog->priv->device = g_value_dup_object (value);
+        case PROP_DRIVE:
+                if (dialog->priv->drive != NULL) {
+                        g_object_unref (dialog->priv->drive);
+                }
+                if (dialog->priv->device != NULL) {
+                        g_signal_handler_disconnect (dialog->priv->device,
+                                                     dialog->priv->device_changed_signal_handler_id);
+                        g_signal_handler_disconnect (dialog->priv->device,
+                                                     dialog->priv->device_job_changed_signal_handler_id);
+                        g_object_unref (dialog->priv->device);
+                }
+                if (g_value_get_object (value) != NULL) {
+                        dialog->priv->drive = g_value_dup_object (value);
+                        dialog->priv->device = gdu_presentable_get_device (GDU_PRESENTABLE (dialog->priv->drive));
+                        if (dialog->priv->device != NULL) {
+                                dialog->priv->device_changed_signal_handler_id = g_signal_connect (dialog->priv->device,
+                                                                                                   "changed",
+                                                                                                   G_CALLBACK (device_changed),
+                                                                                                   dialog);
+                                dialog->priv->device_job_changed_signal_handler_id = g_signal_connect (dialog->priv->device,
+                                                                                                       "job-changed",
+                                                                                                       G_CALLBACK (device_job_changed),
+                                                                                                       dialog);
+                        }
+                } else {
+                        dialog->priv->drive = NULL;
+                        dialog->priv->device = NULL;
+                }
+                update_dialog (dialog);
                 break;
 
         default:
@@ -145,13 +727,9 @@ selection_changed (GtkTreeSelection *tree_selection,
         GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
         GtkTreeIter iter;
         gchar *attr_name;
-        guint n;
 
         attr_name = NULL;
 
-        if (dialog->priv->historical_data == NULL)
-                goto out;
-
         if (!gtk_tree_selection_get_selected (tree_selection,
                                               NULL,
                                               &iter))
@@ -159,268 +737,740 @@ selection_changed (GtkTreeSelection *tree_selection,
 
         gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->attr_list_store),
                             &iter,
-                            ATTR_NAME_COLUMN,
+                            NAME_COLUMN,
                             &attr_name,
                             -1);
 
-        g_debug ("selected %s", attr_name);
-
-        GArray *cur_samples;
-        GArray *worst_samples;
-        GArray *threshold_samples;
-        GArray *raw_samples;
-        GArray *band_samples;
-        GduCurveUnit raw_unit;
-        GList *l;
-        guint64 now;
-
-        cur_samples = g_array_new (FALSE, FALSE, sizeof (GduSample));
-        worst_samples = g_array_new (FALSE, FALSE, sizeof (GduSample));
-        threshold_samples = g_array_new (FALSE, FALSE, sizeof (GduSample));
-        raw_samples = g_array_new (FALSE, FALSE, sizeof (GduSample));
-        band_samples = g_array_new (FALSE, FALSE, sizeof (GduSample));
-        raw_unit = GDU_CURVE_UNIT_INTEGER;
-
-        guint64 tolerance;
-        guint64 timespan;
-
-        timespan = 5 * 24 * 60 * 60;
-        tolerance = 2 * 60 * 60;
-
-        guint64 last_age;
-        now = (guint64) time (NULL);
-        last_age = timespan;
-
-        /* oldest samples first */
-        for (l = dialog->priv->historical_data; l != NULL; l = l->next) {
-                GduAtaSmartHistoricalData *data = GDU_ATA_SMART_HISTORICAL_DATA (l->data);
-                GduAtaSmartAttribute *attr;
-                guint64 time_collected;
-                GduSample sample;
-                guint64 age;
-                gboolean use_sample;
-
-                memset (&sample, '\0', sizeof (GduSample));
-
-                time_collected = gdu_ata_smart_historical_data_get_time_collected (data);
-                age = now - time_collected;
-
-                /* skip old samples, except if the following sample is not too old */
-                use_sample = FALSE;
-                if (age < timespan) {
-                        use_sample = TRUE;
+        //g_debug ("selected %s", attr_name);
+
+ out:
+        g_free (attr_name);
+}
+
+static gchar *
+get_grey_color (GtkTreeView *tree_view,
+                GtkTreeIter *iter)
+{
+        GtkTreeSelection *tree_selection;
+        GtkStyle *style;
+        GdkColor desc_gdk_color = {0};
+        GtkStateType state;
+        gchar *desc_color;
+
+        /* This color business shouldn't be this hard... */
+        tree_selection = gtk_tree_view_get_selection (tree_view);
+        style = gtk_widget_get_style (GTK_WIDGET (tree_view));
+        if (gtk_tree_selection_iter_is_selected (tree_selection, iter)) {
+                if (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (tree_view)))
+                        state = GTK_STATE_SELECTED;
+                else
+                        state = GTK_STATE_ACTIVE;
+        } else {
+                state = GTK_STATE_NORMAL;
+        }
+#define BLEND_FACTOR 0.7
+        desc_gdk_color.red   = style->text[state].red   * BLEND_FACTOR +
+                               style->base[state].red   * (1.0 - BLEND_FACTOR);
+        desc_gdk_color.green = style->text[state].green * BLEND_FACTOR +
+                               style->base[state].green * (1.0 - BLEND_FACTOR);
+        desc_gdk_color.blue  = style->text[state].blue  * BLEND_FACTOR +
+                               style->base[state].blue  * (1.0 - BLEND_FACTOR);
+#undef BLEND_FACTOR
+        desc_color = g_strdup_printf ("#%02x%02x%02x",
+                                      (desc_gdk_color.red >> 8),
+                                      (desc_gdk_color.green >> 8),
+                                      (desc_gdk_color.blue >> 8));
+
+        return desc_color;
+}
+
+static void
+format_markup_name (GtkCellLayout   *cell_layout,
+                    GtkCellRenderer *renderer,
+                    GtkTreeModel    *tree_model,
+                    GtkTreeIter     *iter,
+                    gpointer         user_data)
+{
+        GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
+        SkSmartAttributeParsedData *a;
+        gint id;
+        gchar *name;
+        gchar *desc;
+        gchar *markup;
+        gchar *desc_color;
+
+        gtk_tree_model_get (tree_model,
+                            iter,
+                            ID_COLUMN, &id,
+                            SK_ATTR_COLUMN, &a,
+                            -1);
+
+        attribute_get_details (a,
+                               &name,
+                               &desc);
+
+        if (name == NULL)
+                name = g_strdup (a->name);
+
+        if (desc == NULL) {
+                /* Translators: This is shown in the attribute treeview when no description is found.
+                 * %d is the attribute number.
+                 */
+                desc = g_strdup_printf (_("No description for attribute %d"), id);
+        }
+
+        desc_color = get_grey_color (GTK_TREE_VIEW (dialog->priv->tree_view), iter);
+        if (a->warn) {
+                markup = g_strdup_printf ("<b><span fgcolor='red'>%s</span></b>\n"
+                                          "<span fgcolor='darkred'><small>%s</small></span>",
+                                          name,
+                                          desc);
+        } else {
+                markup = g_strdup_printf ("<b>%s</b>\n"
+                                          "<span fgcolor=\"%s\"><small>%s</small></span>",
+                                          name,
+                                          desc_color,
+                                          desc);
+        }
+
+        g_object_set (renderer,
+                      "markup", markup,
+                      NULL);
+
+        _gdu_sk_attr_free (a);
+        g_free (name);
+        g_free (desc);
+        g_free (markup);
+        g_free (desc_color);
+}
+
+static void
+format_markup_id (GtkCellLayout   *cell_layout,
+                  GtkCellRenderer *renderer,
+                  GtkTreeModel    *tree_model,
+                  GtkTreeIter     *iter,
+                  gpointer         user_data)
+{
+        SkSmartAttributeParsedData *a;
+        gchar *markup;
+        gchar *id;
+
+        gtk_tree_model_get (tree_model,
+                            iter,
+                            SK_ATTR_COLUMN, &a,
+                            -1);
+
+        id = g_strdup_printf ("%d", a->id);
+
+        if (a->warn)
+                markup = g_strdup_printf ("<span foreground='red'>%s</span>", id);
+        else
+                markup = g_strdup_printf ("%s", id);
+
+        g_object_set (renderer,
+                      "markup", markup,
+                      NULL);
+
+        _gdu_sk_attr_free (a);
+        g_free (id);
+        g_free (markup);
+}
+
+static void
+format_markup_value_headings (GtkCellLayout   *cell_layout,
+                              GtkCellRenderer *renderer,
+                              GtkTreeModel    *tree_model,
+                              GtkTreeIter     *iter,
+                              gpointer         user_data)
+{
+        SkSmartAttributeParsedData *a;
+        gchar *markup;
+        const gchar *s1, *s2, *s3, *s4;
+
+        gtk_tree_model_get (tree_model,
+                            iter,
+                            SK_ATTR_COLUMN, &a,
+                            -1);
+
+        /* Translators: This is shown in the tree view for the normalized value of an attribute (0-254) */
+        s1 = _("Normalized:");
+        /* Translators: This is shown in the tree view for the worst value of an attribute (0-254) */
+        s2 = _("Worst:");
+        /* Translators: This is shown in the tree view for the threshold of an attribute (0-254) */
+        s3 = _("Threshold:");
+        /* Translators: This is shown in the tree view for the interpreted/pretty value of an attribute */
+        s4 = _("Value:");
+
+        if (a->warn)
+                markup = g_strdup_printf ("<span foreground='red'>"
+                                          "<small>"
+                                          "%s\n"
+                                          "%s\n"
+                                          "%s\n"
+                                          "%s"
+                                          "</small>"
+                                          "</span>",
+                                          s1, s2, s3, s4);
+        else
+                markup = g_strdup_printf ("<small>"
+                                          "%s\n"
+                                          "%s\n"
+                                          "%s\n"
+                                          "%s"
+                                          "</small>",
+                                          s1, s2, s3, s4);
+
+        g_object_set (renderer,
+                      "markup", markup,
+                      NULL);
+
+        _gdu_sk_attr_free (a);
+        g_free (markup);
+}
+
+static void
+format_markup_value (GtkCellLayout   *cell_layout,
+                     GtkCellRenderer *renderer,
+                     GtkTreeModel    *tree_model,
+                     GtkTreeIter     *iter,
+                     gpointer         user_data)
+{
+        SkSmartAttributeParsedData *a;
+        gchar *markup;
+        gchar *s1, *s2, *s3, *s4;
+
+        gtk_tree_model_get (tree_model,
+                            iter,
+                            SK_ATTR_COLUMN, &a,
+                            -1);
+
+        if (a->worst_value_valid) {
+                s1 = g_strdup_printf ("%d", a->current_value);
+        } else {
+                /* Translators: This is used in the attribute treview when normalized/worst/treshold
+                 * value isn't available */
+                s1 = g_strdup (_("N/A"));
+        }
+
+        if (a->worst_value_valid) {
+                s2 = g_strdup_printf ("%d", a->worst_value);
+        } else {
+                /* Translators: This is used in the attribute treview when normalized/worst/treshold
+                 * value isn't available */
+                s2 = g_strdup (_("N/A"));
+        }
+
+        if (a->threshold_valid) {
+                s3 = g_strdup_printf ("%d", a->threshold);
+        } else {
+                /* Translators: This is used in the attribute treview when normalized/worst/treshold
+                 * value isn't available */
+                s3 = g_strdup (_("N/A"));
+        }
+
+        s4 = pretty_to_string (a->pretty_value, a->pretty_unit);
+
+        if (a->warn)
+                markup = g_strdup_printf ("<span foreground='red'>"
+                                          "<small>"
+                                          "%s\n"
+                                          "%s\n"
+                                          "%s\n"
+                                          "%s"
+                                          "</small>"
+                                          "</span>",
+                                          s1, s2, s3, s4);
+        else
+                markup = g_strdup_printf ("<small>"
+                                          "%s\n"
+                                          "%s\n"
+                                          "%s\n"
+                                          "%s"
+                                          "</small>",
+                                          s1, s2, s3, s4);
+
+        g_object_set (renderer,
+                      "markup", markup,
+                      NULL);
+
+        _gdu_sk_attr_free (a);
+        g_free (s1);
+        g_free (s2);
+        g_free (s3);
+        g_free (s4);
+        g_free (markup);
+}
+
+static void
+format_markup_assessment (GtkCellLayout   *cell_layout,
+                          GtkCellRenderer *renderer,
+                          GtkTreeModel    *tree_model,
+                          GtkTreeIter     *iter,
+                          gpointer         user_data)
+{
+        SkSmartAttributeParsedData *a;
+        gchar *markup;
+        const gchar *assessment;
+        gboolean failed;
+        gboolean failed_in_the_past;
+
+        gtk_tree_model_get (tree_model,
+                            iter,
+                            SK_ATTR_COLUMN, &a,
+                            -1);
+
+        failed = FALSE;
+        failed_in_the_past = FALSE;
+        if (a->prefailure) {
+                if (!a->good_now && a->good_now_valid) {
+                        failed = TRUE;
+                }
+
+                if (!a->good_in_the_past && a->good_in_the_past_valid) {
+                        failed_in_the_past = TRUE;
+                }
+        } else {
+                if (a->current_value_valid && a->threshold_valid &&
+                    a->current_value <= a->threshold) {
+                        failed = TRUE;
+                }
+
+                if (a->worst_value_valid && a->threshold_valid &&
+                    a->worst_value <= a->threshold) {
+                        failed_in_the_past = TRUE;
+                }
+        }
+
+        if (failed) {
+                /* Translators: Shown in the treeview for a failing attribute */
+                assessment = _("Failing");
+        } else if (failed_in_the_past) {
+                /* Translators: Shown in the treeview for an attribute that failed in the past */
+                assessment = _("Failed in the past");
+        } else if (a->good_now_valid) {
+                if (a->warn) {
+                        /* Translators: Shown in the treeview for an attribute that we want to warn about */
+                        assessment = _("Warning");
                 } else {
-                        if (l->next != NULL) {
-                                GduAtaSmartHistoricalData *next_data = GDU_ATA_SMART_HISTORICAL_DATA (l->next->data);
-                                guint64 next_age;
-                                next_age = now - gdu_ata_smart_historical_data_get_time_collected (next_data);
-                                if (next_age < timespan) {
-                                        use_sample = TRUE;
-                                }
-                        }
+                        /* Translators: Shown in the treeview for an attribute that is good */
+                        assessment = _("Good");
                 }
+        } else {
+                if (a->warn) {
+                        /* Translators: Shown in the treeview for an attribute that we want to warn about */
+                        assessment = _("Warning");
+                } else {
+                        /* Translators: Shown in the treeview for an attribute we don't know the status about */
+                        assessment = _("N/A");
+                }
+        }
 
-                if (use_sample) {
-
-                        sample.time_usec = time_collected * 1000L * 1000L;
-
-                        attr = gdu_ata_smart_historical_data_get_attribute (data, attr_name);
-                        if (attr != NULL) {
-                                guint current;
-                                guint worst;
-                                guint threshold;
-                                guint64 raw;
-
-                                current = gdu_ata_smart_attribute_get_current (attr);
-                                worst = gdu_ata_smart_attribute_get_worst (attr);
-                                threshold = gdu_ata_smart_attribute_get_threshold (attr);
-                                raw = gdu_ata_smart_attribute_get_pretty_value (attr);
-
-                                sample.value = current / 255.0f;
-                                g_array_append_val (cur_samples, sample);
-
-                                sample.value = worst / 255.0f;
-                                g_array_append_val (worst_samples, sample);
-
-                                sample.value = threshold / 255.0f;
-                                g_array_append_val (threshold_samples, sample);
-
-                                switch (gdu_ata_smart_attribute_get_pretty_unit (attr)) {
-                                case GDU_ATA_SMART_ATTRIBUTE_UNIT_MSECONDS:
-                                        sample.value = ((gdouble) raw) / 1000.0;
-                                        raw_unit = GDU_CURVE_UNIT_TIME_SECONDS;
-                                        break;
-                                case GDU_ATA_SMART_ATTRIBUTE_UNIT_MKELVIN:
-                                        sample.value = ((gdouble) raw) / 1000.0;
-                                        raw_unit = GDU_CURVE_UNIT_TEMPERATURE_KELVIN;
-                                        break;
-                                default:
-                                case GDU_ATA_SMART_ATTRIBUTE_UNIT_UNKNOWN:
-                                case GDU_ATA_SMART_ATTRIBUTE_UNIT_NONE:
-                                case GDU_ATA_SMART_ATTRIBUTE_UNIT_SECTORS:
-                                        sample.value = (gdouble) raw;
-                                        raw_unit = GDU_CURVE_UNIT_INTEGER;
-                                        break;
-                                }
+        if (a->warn)
+                markup = g_strdup_printf ("<span foreground='red'>%s</span>", assessment);
+        else
+                markup = g_strdup_printf ("%s", assessment);
 
-                                g_array_append_val (raw_samples, sample);
+        g_object_set (renderer,
+                      "markup", markup,
+                      NULL);
 
-                                g_object_unref (attr);
-                        }
+        _gdu_sk_attr_free (a);
+        g_free (markup);
+}
+
+static void
+pixbuf_assessment (GtkCellLayout   *cell_layout,
+                   GtkCellRenderer *renderer,
+                   GtkTreeModel    *tree_model,
+                   GtkTreeIter     *iter,
+                   gpointer         user_data)
+{
+        SkSmartAttributeParsedData *a;
+        const gchar *icon_name;
+
+        gtk_tree_model_get (tree_model,
+                            iter,
+                            SK_ATTR_COLUMN, &a,
+                            -1);
+
+        if (a->warn) {
+                icon_name = "gdu-smart-failing";
+        } else {
+                if (a->good_now_valid)
+                        icon_name = "gdu-smart-healthy";
+                else
+                        icon_name = "gdu-smart-unknown";
+        }
+
+        g_object_set (renderer,
+                      "icon-name", icon_name,
+                      "stock-size", GTK_ICON_SIZE_MENU,
+                      NULL);
+
+        _gdu_sk_attr_free (a);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+refresh_cb (GduDevice  *device,
+            GError     *error,
+            gpointer    user_data)
+{
+        GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
+
+        /* TODO: maybe show error dialog */
+        if (error != NULL)
+                g_error_free (error);
+
+        dialog->priv->is_updating = FALSE;
+        update_dialog (dialog);
+}
+
+
+static void
+on_activate_link_update_smart_data (GtkLabel    *label,
+                                    const gchar *uri,
+                                    gpointer     user_data)
+{
+        GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
 
-                        /* draw a band if there's a discontinuity; e.g. no samples for an hour or more */
-                        if (last_age - age >= tolerance) {
-                                sample.time_usec = (now - last_age + tolerance) * 1000 * 1000;
-                                sample.value = 1.0;
-                                g_array_append_val (band_samples, sample);
+        gdu_device_drive_ata_smart_refresh_data (dialog->priv->device,
+                                                 refresh_cb,
+                                                 dialog);
 
-                                sample.time_usec = (now - age - tolerance) * 1000 * 1000;
-                                sample.value = 1.0;
-                                g_array_append_val (band_samples, sample);
+        g_signal_stop_emission_by_name (label, "activate-link");
+
+        dialog->priv->is_updating = TRUE;
+        update_dialog (dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+cancel_self_test_cb (GduDevice  *device,
+                     GError     *error,
+                     gpointer    user_data)
+{
+        /* TODO: maybe show error dialog */
+        if (error != NULL)
+                g_error_free (error);
+}
+
+
+static void
+on_activate_link_cancel_self_test (GtkLabel    *link_label,
+                                   const gchar *uri,
+                                   gpointer     user_data)
+{
+        GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
+
+        g_signal_stop_emission_by_name (link_label, "activate-link");
+
+        gdu_device_op_cancel_job (dialog->priv->device,
+                                  cancel_self_test_cb,
+                                  dialog);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+run_self_test_cb (GduDevice  *device,
+                  GError     *error,
+                  gpointer    user_data)
+{
+        /* TODO: maybe show error dialog */
+        if (error != NULL)
+                g_error_free (error);
+}
+
+
+static void
+on_activate_link_run_self_test (GtkLabel    *link_label,
+                                const gchar *uri,
+                                gpointer     user_data)
+{
+        GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
+        GtkWidget *test_dialog;
+        GtkWidget *hbox;
+        GtkWidget *image;
+        GtkWidget *main_vbox;
+        GtkWidget *label;
+        GtkWidget *radio0;
+        GtkWidget *radio1;
+        GtkWidget *radio2;
+        gchar *s;
+        gint response;
+        const gchar *test;
+
+        g_signal_stop_emission_by_name (link_label, "activate-link");
+
+        test_dialog = gtk_dialog_new_with_buttons (NULL,
+                                                   GTK_WINDOW (dialog),
+                                                   GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_NO_SEPARATOR,
+                                                   NULL);
+        gtk_window_set_title (GTK_WINDOW (test_dialog), "");
+
+	gtk_container_set_border_width (GTK_CONTAINER (test_dialog), 6);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (test_dialog)->vbox), 2);
+	gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (test_dialog)->action_area), 5);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (test_dialog)->action_area), 6);
+	gtk_window_set_resizable (GTK_WINDOW (test_dialog), FALSE);
+
+	hbox = gtk_hbox_new (FALSE, 12);
+	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (test_dialog)->vbox), hbox, TRUE, TRUE, 0);
+
+	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+
+	main_vbox = gtk_vbox_new (FALSE, 10);
+	gtk_box_pack_start (GTK_BOX (hbox), main_vbox, TRUE, TRUE, 0);
+
+	label = gtk_label_new (NULL);
+        s = g_strconcat ("<big><b>",
+                         /* Translators: Shown in the "Run self-test" dialog */
+                         _("Select what SMART self test to run"),
+                         "</b></big>",
+                         NULL);
+        gtk_label_set_markup (GTK_LABEL (label), s);
+        g_free (s);
+	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+	gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (label), FALSE, FALSE, 0);
+
+	label = gtk_label_new (NULL);
+        /* Translators: Shown in the "Run self-test" dialog */
+        gtk_label_set_markup (GTK_LABEL (label), _("The tests may take a very long time to complete depending "
+                                                   "on the speed and size of the disk. You can continue using "
+                                                   "your system while the test is running."));
+	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+	gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (label), FALSE, FALSE, 0);
+
+        radio0 = gtk_radio_button_new_with_mnemonic_from_widget (NULL,
+                                                                 /* Translators: Radio button for short test */
+                                                                 _("_Short (usually less than ten minutes)"));
+        radio1 = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON (radio0),
+                                                                 /* Translators: Radio button for extended test */
+                                                                 _("_Extended (usually tens of minutes)"));
+        radio2 = gtk_radio_button_new_with_mnemonic_from_widget (GTK_RADIO_BUTTON (radio0),
+                                                                 /* Translators: Radio button for conveyance test */
+                                                                 _("C_onveyance (usually less than ten minutes)"));
+
+	gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (radio0), FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (radio1), FALSE, FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (radio2), FALSE, FALSE, 0);
+
+        gtk_dialog_add_button (GTK_DIALOG (test_dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+        /* Translators: Button in "Run self-test dialog" */
+        gtk_dialog_add_button (GTK_DIALOG (test_dialog), _("_Initiate Self Test"), 0);
+        gtk_dialog_set_default_response (GTK_DIALOG (test_dialog), 0);
+
+        gtk_widget_show_all (test_dialog);
+        response = gtk_dialog_run (GTK_DIALOG (test_dialog));
+
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio0))) {
+                test = "short";
+        } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio1))) {
+                test = "extended";
+        } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio2))) {
+                test = "conveyance";
+        }
+
+        gtk_widget_destroy (test_dialog);
+        if (response != 0)
+                goto out;
+
+        gdu_device_op_drive_ata_smart_initiate_selftest (dialog->priv->device,
+                                                         test,
+                                                         run_self_test_cb,
+                                                         dialog);
+
+ out:
+        ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+disk_name_data_func (GtkCellLayout   *cell_layout,
+                     GtkCellRenderer *renderer,
+                     GtkTreeModel    *tree_model,
+                     GtkTreeIter     *iter,
+                     gpointer         user_data)
+{
+        gchar *name;
+        gchar *vpd_name;
+        gchar *markup;
+        gchar *desc;
+        GduPresentable *p;
+        GduDevice *d;
+        gboolean sensitive;
+        gchar *s;
+
+        gtk_tree_model_get (tree_model,
+                            iter,
+                            GDU_POOL_TREE_MODEL_COLUMN_NAME, &name,
+                            GDU_POOL_TREE_MODEL_COLUMN_VPD_NAME, &vpd_name,
+                            GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, &p,
+                            -1);
 
-                                /* close the segment */
-                                sample.time_usec = G_MAXINT64;
-                                sample.value = G_MAXDOUBLE;
-                                g_array_append_val (band_samples, sample);
+        d = gdu_presentable_get_device (p);
+
+        desc = NULL;
+        sensitive = FALSE;
+        if (d != NULL) {
+                if (gdu_device_drive_ata_smart_get_is_available (d) &&
+                    gdu_device_drive_ata_smart_get_time_collected (d) > 0) {
+                        const gchar *status;
+                        gboolean highlight;
+
+                        sensitive = TRUE;
+
+                        status = gdu_device_drive_ata_smart_get_status (d);
+                        if (status != NULL && strlen (status) > 0) {
+                                desc = gdu_util_ata_smart_status_to_desc (status, &highlight, NULL, NULL);
+                                if (highlight) {
+                                        s = g_strdup_printf ("<span fgcolor=\"red\"><b>%s</b></span>", desc);
+                                        g_free (desc);
+                                        desc = s;
+                                }
+                        } else if (gdu_device_drive_ata_smart_get_is_available (d) &&
+                                   gdu_device_drive_ata_smart_get_time_collected (d) > 0) {
+                                /* Translators: Used in the drive combo-box to indicate the health status is unknown */
+                                desc = g_strdup (_("Health status is unknown"));
+                        }
+                } else {
+                        if (gdu_device_drive_ata_smart_get_is_available (d)) {
+                                /* Translators: Used in the drive combo-box to indicate SMART is not enabled */
+                                desc = g_strdup (_("SMART is not enabled"));
+                        } else {
+                                /* Translators: Used in the drive combo-box to indicate SMART is not available */
+                                desc = g_strdup (_("SMART is not available"));
                         }
                 }
+        }
 
-                last_age = age;
+        if (desc != NULL) {
+                markup = g_strdup_printf ("<b>%s</b> â?? %s\n"
+                                          "<small>%s</small>",
+                                          name,
+                                          vpd_name,
+                                          desc);
+        } else {
+                markup = g_strdup_printf ("<b>%s</b> â?? %s\n"
+                                          "<small> </small>",
+                                          name,
+                                          vpd_name);
         }
 
-#define GDU_COLOR_FROM_HEX(argb_hex, alpha)             \
-        {                                               \
-                (((argb_hex) >> 16)&0xff) / 255.0,      \
-                (((argb_hex) >>  8)&0xff) / 255.0,      \
-                (((argb_hex) >>  0)&0xff) / 255.0,      \
-                alpha / 255.0                           \
+        g_object_set (renderer,
+                      "markup", markup,
+                      "sensitive", sensitive,
+                      NULL);
+
+        g_free (name);
+        g_free (vpd_name);
+        g_free (markup);
+        g_free (desc);
+        g_object_unref (p);
+        if (d != NULL)
+                g_object_unref (d);
+}
+
+static void
+disk_name_gicon_func (GtkCellLayout   *cell_layout,
+                      GtkCellRenderer *renderer,
+                      GtkTreeModel    *tree_model,
+                      GtkTreeIter     *iter,
+                      gpointer         user_data)
+{
+        GIcon *icon;
+        GduPresentable *p;
+        GduDevice *d;
+        gboolean sensitive;
+
+        gtk_tree_model_get (tree_model,
+                            iter,
+                            GDU_POOL_TREE_MODEL_COLUMN_ICON, &icon,
+                            GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, &p,
+                            -1);
+
+        d = gdu_presentable_get_device (p);
+        sensitive = FALSE;
+        if (d != NULL) {
+                if (gdu_device_drive_ata_smart_get_is_available (d) &&
+                    gdu_device_drive_ata_smart_get_time_collected (d) > 0)
+                        sensitive = TRUE;
         }
 
-        /* see http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines for colors
-         */
-        GduColor raw_color       = GDU_COLOR_FROM_HEX (0xfcaf3e, 255.0); /* orange */
-        GduColor raw_fill_color  = GDU_COLOR_FROM_HEX (0xfcaf3e, 128.0); /* orange */
-        GduColor cur_color       = GDU_COLOR_FROM_HEX (0x729fcf, 255.0); /* sky blue */
-        GduColor worst_color     = GDU_COLOR_FROM_HEX (0xad7fa8, 255.0); /* plum */
-        GduColor threshold_color = GDU_COLOR_FROM_HEX (0xef2929, 255.0); /* scarlet red */
-        GduColor band_color      = { 0.00, 0.00, 0.00, 0.0 };
-        GduColor band_fill_color = { 0.85, 0.85, 0.85, 0.5 };
-        GduCurve *c;
-
-        /* add graphs in order */
-        gint z_order = 0;
-
-        /* bands representing no data */
-        c = gdu_curve_new ();
-        gdu_curve_set_legend (c, _("No data"));
-        gdu_curve_set_z_order (c, z_order++);
-        gdu_curve_set_samples (c, band_samples);
-        gdu_curve_set_color (c, &band_color);
-        gdu_curve_set_fill_color (c, &band_fill_color);
-        gdu_curve_set_flags (c, GDU_CURVE_FLAGS_FILLED | GDU_CURVE_FLAGS_FADE_EDGES);
-        gdu_graph_add_curve (GDU_GRAPH (dialog->priv->graph),
-                             "band",
-                             c);
-        g_object_unref (c);
-
-        /* worst */
-        c = gdu_curve_new ();
-        gdu_curve_set_legend (c, _("Worst"));
-        gdu_curve_set_z_order (c, z_order++);
-        gdu_curve_set_samples (c, worst_samples);
-        gdu_curve_set_color (c, &worst_color);
-        gdu_curve_set_width (c, 1.0);
-        gdu_graph_add_curve (GDU_GRAPH (dialog->priv->graph),
-                             "worst",
-                             c);
-        g_object_unref (c);
-
-        /* threshold */
-        c = gdu_curve_new ();
-        gdu_curve_set_legend (c, _("Treshold"));
-        gdu_curve_set_z_order (c, z_order++);
-        gdu_curve_set_samples (c, threshold_samples);
-        gdu_curve_set_color (c, &threshold_color);
-        gdu_curve_set_width (c, 1.0);
-        gdu_graph_add_curve (GDU_GRAPH (dialog->priv->graph),
-                             "threshold",
-                             c);
-        g_object_unref (c);
-
-        /* current */
-        c = gdu_curve_new ();
-        gdu_curve_set_legend (c, _("Current"));
-        gdu_curve_set_z_order (c, z_order++);
-        gdu_curve_set_samples (c, cur_samples);
-        gdu_curve_set_color (c, &cur_color);
-        gdu_curve_set_width (c, 2.0);
-        gdu_graph_add_curve (GDU_GRAPH (dialog->priv->graph),
-                             "current",
-                             c);
-        g_object_unref (c);
-
-
-        /* raw */
-        c = gdu_curve_new ();
-        gdu_curve_set_legend (c, _("Raw")); /* TODO: units? */
-        gdu_curve_set_z_order (c, z_order++);
-        gdu_curve_set_samples (c, raw_samples);
-        gdu_curve_set_unit (c, raw_unit);
-        gdu_curve_set_color (c, &raw_color);
-        gdu_curve_set_fill_color (c, &raw_fill_color);
-        gdu_curve_set_width (c, 2.0);
-        gdu_curve_set_flags (c,
-                             GDU_CURVE_FLAGS_AXIS_MARKERS_LEFT |
-                             GDU_CURVE_FLAGS_FILLED |
-                             GDU_CURVE_FLAGS_NORMALIZE);
-        gdu_graph_add_curve (GDU_GRAPH (dialog->priv->graph),
-                             "raw",
-                             c);
-        g_object_unref (c);
-
-        g_array_unref (cur_samples);
-        g_array_unref (worst_samples);
-        g_array_unref (threshold_samples);
-        g_array_unref (raw_samples);
-        g_array_unref (band_samples);
+        g_object_set (renderer,
+                      "gicon", icon,
+                      "sensitive", sensitive,
+                      NULL);
 
- out:
-        g_free (attr_name);
+        g_object_unref (icon);
+        g_object_unref (p);
+        if (d != NULL)
+                g_object_unref (d);
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
-on_period_scale_changed (GtkRange *range,
-                         gpointer  user_data)
+on_drive_combo_box_changed (GtkComboBox *combo_box,
+                            gpointer     user_data)
 {
-        gdouble value;
         GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
+        GtkTreeIter iter = {0};
 
-        value = gtk_range_get_value (range);
+        if (gtk_combo_box_get_active_iter (combo_box, &iter)) {
+                GduPresentable *p;
 
-        gdu_graph_set_window_size_usec (GDU_GRAPH (dialog->priv->graph),
-                                        value * 60 * 60 * G_USEC_PER_SEC);
+                gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->pool_tree_model),
+                                    &iter,
+                                    GDU_POOL_TREE_MODEL_COLUMN_PRESENTABLE, &p,
+                                    -1);
+
+                g_object_set (dialog,
+                              "drive", GDU_DRIVE (p),
+                              NULL);
+
+                g_object_unref (p);
+        } else {
+                g_object_set (dialog,
+                              "drive", NULL,
+                              NULL);
+        }
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
-on_end_scale_changed (GtkRange *range,
-                      gpointer  user_data)
+on_no_warn_check_button_toggled (GtkToggleButton *toggle_button,
+                                 gpointer         user_data)
 {
-        gdouble value;
         GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
-        gint64 now_usec;
+        gboolean is_active;
 
-        now_usec = time (NULL) * G_USEC_PER_SEC;
+        is_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->priv->no_warn_check_button));
 
-        value = gtk_range_get_value (range);
-        if (value < 1.0) {
-                gdu_graph_set_window_end_usec (GDU_GRAPH (dialog->priv->graph),
-                                               G_MAXINT64);
-        } else {
-                gdu_graph_set_window_end_usec (GDU_GRAPH (dialog->priv->graph),
-                                               now_usec - value * 60 * 60 * G_USEC_PER_SEC);
+        if (dialog->priv->device == NULL)
+                goto out;
+
+        if (get_ata_smart_no_warn (dialog->priv->device) != is_active) {
+                set_ata_smart_no_warn (dialog->priv->device, is_active);
+                update_dialog (dialog);
         }
+
+ out:
+        ;
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
 gdu_ata_smart_dialog_constructed (GObject *object)
 {
@@ -428,217 +1478,501 @@ gdu_ata_smart_dialog_constructed (GObject *object)
         GtkWidget *content_area;
         GtkWidget *align;
         GtkWidget *vbox;
+        GtkWidget *vbox2;
         GtkWidget *hbox;
+        GtkWidget *image;
         GtkWidget *table;
         GtkWidget *label;
         GtkWidget *tree_view;
         GtkWidget *scrolled_window;
-        GtkWidget *graph;
+        GtkWidget *spinner;
+        GtkWidget *progress_bar;
+        GtkWidget *check_button;
         GtkCellRenderer *renderer;
         GtkTreeViewColumn *column;
         gint row;
-        GduPresentable *drive;
-        GduPool *pool;
-        gchar *title;
-        gchar *drive_name;
         GtkTreeSelection *selection;
+        gchar *s;
+        GtkWidget *combo_box;
+        GduPool *pool;
+        GtkTreeIter iter = {0};
+        const gchar *tooltip_markup;
+        gboolean rtl;
 
-        gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+        rtl = (gtk_widget_get_direction (GTK_WIDGET (dialog)) == GTK_TEXT_DIR_RTL);
 
-        pool = gdu_device_get_pool (dialog->priv->device);
-        drive = gdu_pool_get_drive_by_device (pool, dialog->priv->device);
-        drive_name = gdu_presentable_get_name (drive);
-        /* Translators: %s is the drive name */
-        title = g_strdup_printf (_("ATA SMART data for %s"), drive_name);
-        gtk_window_set_title (GTK_WINDOW (dialog), title);
-        g_object_unref (pool);
-        g_object_unref (drive);
-        g_free (title);
-        g_free (drive_name);
+        /* Translators: Title of the SMART dialog */
+        gtk_window_set_title (GTK_WINDOW (dialog), _("SMART Data"));
+        gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
 
         content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
-        //gtk_dialog_add_button (GTK_DIALOG (dialog),
-        //                       GTK_STOCK_CLOSE,
-        //                       GTK_RESPONSE_CLOSE);
 
         align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
         gtk_alignment_set_padding (GTK_ALIGNMENT (align), 12, 12, 12, 12);
         gtk_box_pack_start (GTK_BOX (content_area), align, TRUE, TRUE, 0);
 
-        vbox = gtk_vbox_new (FALSE, 6);
+        vbox = gtk_vbox_new (FALSE, 12);
         gtk_container_add (GTK_CONTAINER (align), vbox);
 
-        hbox = gtk_hbox_new (FALSE, 6);
-        gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+        /* ---------------------------------------------------------------------------------------------------- */
+
+        pool = gdu_device_get_pool (dialog->priv->device);
+        dialog->priv->pool_tree_model = gdu_pool_tree_model_new (pool,
+                                                                 GDU_POOL_TREE_MODEL_FLAGS_NO_VOLUMES);
+        g_object_unref (pool);
 
         table = gtk_table_new (4, 2, FALSE);
         gtk_table_set_col_spacings (GTK_TABLE (table), 12);
-        gtk_table_set_row_spacings (GTK_TABLE (table), 4);
-        gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
-
-        graph = gdu_graph_new ();
-        dialog->priv->graph = graph;
-        gtk_widget_set_size_request (graph, 480, 350);
-        gtk_box_pack_start (GTK_BOX (hbox), graph, TRUE, TRUE, 0);
-
-#if 0
-        const gchar *time_axis[7];
-        time_axis[0] = C_("ATA SMART graph label", "five days ago");
-        time_axis[1] = C_("ATA SMART graph label", "four days ago");
-        time_axis[2] = C_("ATA SMART graph label", "three days ago");
-        time_axis[3] = C_("ATA SMART graph label", "two days ago");
-        time_axis[4] = C_("ATA SMART graph label", "one day ago");
-        time_axis[5] = C_("ATA SMART graph label", "now");
-        time_axis[6] = NULL;
-        gdu_graph_set_x_markers (GDU_GRAPH (graph), time_axis);
-
-        const gchar *y_axis_right[6];
-        y_axis_right[0] = C_("ATA SMART graph label", "0");
-        y_axis_right[1] = C_("ATA SMART graph label", "64");
-        y_axis_right[2] = C_("ATA SMART graph label", "128");
-        y_axis_right[3] = C_("ATA SMART graph label", "192");
-        y_axis_right[4] = C_("ATA SMART graph label", "255");
-        y_axis_right[5] = NULL;
-        gdu_graph_set_y_markers_right (GDU_GRAPH (graph), y_axis_right);
-#endif
+        gtk_table_set_row_spacings (GTK_TABLE (table), 0);
+        gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
 
         row = 0;
 
-        /* power on hours */
+        /* ------------------------------ */
+
         label = gtk_label_new (NULL);
-        gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
-        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Powered On:</b>"));
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        /* Translators: Label used before the drive combo box */
+        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("_Drive:"));
         gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
+        combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (dialog->priv->pool_tree_model));
+        gtk_table_attach (GTK_TABLE (table), combo_box, 1, 2, row, row + 1,
+                          GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+        dialog->priv->drive_combo_box = combo_box;
+        gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo_box);
+        g_signal_connect (combo_box,
+                          "changed",
+                          G_CALLBACK (on_drive_combo_box_changed),
+                          dialog);
+
+        renderer = gtk_cell_renderer_pixbuf_new ();
+        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, FALSE);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box),
+                                            renderer,
+                                            disk_name_gicon_func,
+                                            dialog,
+                                            NULL);
+        g_object_set (renderer,
+                      "stock-size", GTK_ICON_SIZE_SMALL_TOOLBAR,
+                      NULL);
+
+        renderer = gtk_cell_renderer_text_new ();
+        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box),
+                                            renderer,
+                                            disk_name_data_func,
+                                            dialog,
+                                            NULL);
+
+        row++;
+
+        if (dialog->priv->drive != NULL) {
+                if (gdu_pool_tree_model_get_iter_for_presentable (dialog->priv->pool_tree_model,
+                                                                  GDU_PRESENTABLE (dialog->priv->drive),
+                                                                  &iter)) {
+                        gtk_combo_box_set_active_iter (GTK_COMBO_BOX (dialog->priv->drive_combo_box), &iter);
+                }
+        }
+
+        /* ---------------------------------------------------------------------------------------------------- */
+
         label = gtk_label_new (NULL);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->power_on_hours_label = label;
+        /* Translators: Heading used in the main dialog for the SMART status */
+        s = g_strconcat ("<b>", _("Status"), "</b>", NULL);
+        gtk_label_set_markup (GTK_LABEL (label), s);
+        g_free (s);
+        gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
 
-        gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
+        align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 0);
+        gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 0);
+
+        vbox2 = gtk_vbox_new (FALSE, 6);
+        gtk_container_add (GTK_CONTAINER (align), vbox2);
+
+        table = gtk_table_new (4, 2, FALSE);
+        gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+        gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+        gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
+
+        row = 0;
+
+        /* ------------------------------ */
+        /* updated */
+
+        /* Translators: Tooltip for the Updated item in the status table */
+        tooltip_markup = _("Time since SMART data was last read â?? SMART data is updated every 30 minutes unless "
+                           "the disk is sleeping");
+
+        label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        /* Translators: Item name in the status table */
+        gtk_label_set_markup (GTK_LABEL (label), _("Updated:"));
+        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
+        hbox = gtk_hbox_new (FALSE, 0);
+
+        /* Translators: Used in the status table when data is currently being updated */
+        label = gtk_label_new (_("Updating..."));
+        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+        dialog->priv->updating_label = label;
+
+        spinner = gdu_spinner_new ();
+        gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 6);
+        dialog->priv->updating_spinner = spinner;
+
+        label = gdu_time_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        dialog->priv->updated_label = label;
+        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+
+        label = gtk_label_new (NULL);
+        s = g_strdup_printf (rtl ? "<a href=\"update-now\" title=\"%s\">%s</a> â?? " :
+                                   " â?? <a href=\"update-now\" title=\"%s\">%s</a>",
+                             /* Translators: Tooltip for the "Update Now" hyperlink */
+                             _("Reads SMART data from the disk, waking it up if necessary"),
+                            /* Translators: Text used in the hyperlink in the status table to update the SMART status */
+                             _("Update now"));
+        gtk_label_set_track_visited_links (GTK_LABEL (label), FALSE);
+        gtk_label_set_markup (GTK_LABEL (label), s);
+        g_free (s);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        dialog->priv->update_link_label = label;
+        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+        g_signal_connect (label,
+                          "activate-link",
+                          G_CALLBACK (on_activate_link_update_smart_data),
+                          dialog);
+
+        gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, row, row + 1,
+                          GTK_FILL, GTK_FILL, 0, 0);
         row++;
 
-        /* temperature */
+        /* control visibility (see update_dialog()) */
+        gtk_widget_set_no_show_all (dialog->priv->updated_label, TRUE);
+        gtk_widget_set_no_show_all (dialog->priv->updating_label, TRUE);
+        gtk_widget_set_no_show_all (dialog->priv->updating_spinner, TRUE);
+        gtk_widget_set_no_show_all (dialog->priv->update_link_label, TRUE);
+
+        /* ------------------------------ */
+        /* self-tests */
+
+        /* Translators: Tooltip for the Self-tests item in the status table */
+        tooltip_markup = _("The result of the last self-test that ran on the disk");
+
         label = gtk_label_new (NULL);
-        gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
-        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Temperature:</b>"));
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        /* Translators: Item name in the status table */
+        gtk_label_set_markup (GTK_LABEL (label), _("Self-tests:"));
         gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
+        hbox = gtk_hbox_new (FALSE, 0);
+
         label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->temperature_label = label;
+        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+        dialog->priv->self_test_result_label = label;
+
+
+        progress_bar = gtk_progress_bar_new ();
+        gtk_box_pack_start (GTK_BOX (hbox), progress_bar, FALSE, FALSE, 0);
+        dialog->priv->self_test_progress_bar = progress_bar;
+
+        label = gtk_label_new (NULL);
+        s = g_strdup_printf (rtl ? "<a href=\"run-self-test\" title=\"%s\">%s</a> â?? " :
+                                   " â?? <a href=\"run-self-test\" title=\"%s\">%s</a>",
+                             /* Translators: Tooltip for the "Run self-test" hyperlink */
+                             _("Initiates a self-test on the drive"),
+                             /* Translators: Text used in the hyperlink in the status table to run a self-test */
+                             _("Run self-test"));
+        gtk_label_set_track_visited_links (GTK_LABEL (label), FALSE);
+        gtk_label_set_markup (GTK_LABEL (label), s);
+        g_free (s);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+        dialog->priv->self_test_run_link_label = label;
+        g_signal_connect (label,
+                          "activate-link",
+                          G_CALLBACK (on_activate_link_run_self_test),
+                          dialog);
+
+        label = gtk_label_new (NULL);
+        gtk_label_set_track_visited_links (GTK_LABEL (label), FALSE);
+        s = g_strdup_printf (rtl ? "<a href=\"cancel-self-test\" title=\"%s\">%s</a> â?? " :
+                                   " â?? <a href=\"cancel-self-test\" title=\"%s\">%s</a>",
+                             /* Translators: Tooptip for the "Cancel" hyperlink */
+                             _("Cancels the currently running test"),
+                             /* Translators: Text used in the hyperlink in the status table to cancel a self-test */
+                             _("Cancel"));
+        gtk_label_set_markup (GTK_LABEL (label), s);
+        g_free (s);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+        dialog->priv->self_test_cancel_link_label = label;
+        g_signal_connect (label,
+                          "activate-link",
+                          G_CALLBACK (on_activate_link_cancel_self_test),
+                          dialog);
+
+        gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, row, row + 1,
+                          GTK_FILL, GTK_FILL, 0, 0);
+
+        row++;
+
+        /* control visibility (see update_dialog()) */
+        gtk_widget_set_no_show_all (dialog->priv->self_test_result_label, TRUE);
+        gtk_widget_set_no_show_all (dialog->priv->self_test_progress_bar, TRUE);
+        gtk_widget_set_no_show_all (dialog->priv->self_test_run_link_label, TRUE);
+        gtk_widget_set_no_show_all (dialog->priv->self_test_cancel_link_label, TRUE);
+
+        /* ------------------------------ */
+        /* model */
+
+        /* Translators: Tooltip for the "Model Name:" item in the status table */
+        tooltip_markup = _("The name of the model of the disk");
+
+        label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        /* Translators: Item name in the status table */
+        gtk_label_set_markup (GTK_LABEL (label), _("Model Name:"));
+        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+                          GTK_FILL, GTK_FILL, 0, 0);
+
+        label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        dialog->priv->model_label = label;
 
         gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
         row++;
 
-        /* last test */
+        /* ------------------------------ */
+        /* firmware */
+
+        /* Translators: Tooltip for the "Firmware Version:" item in the status table */
+        tooltip_markup = _("The firmware version of the disk");
+
         label = gtk_label_new (NULL);
-        gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
-        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Last Test:</b>"));
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        /* Translators: Item name in the status table */
+        gtk_label_set_markup (GTK_LABEL (label), _("Firmware Version:"));
         gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
         label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->last_self_test_result_label = label;
+        dialog->priv->firmware_label = label;
 
         gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
         row++;
 
-        /* updated */
+        /* ------------------------------ */
+        /* serial */
+
+        /* Translators: Tooltip for the "Serial:" item in the status table */
+        tooltip_markup = _("The serial number of the disk");
+
         label = gtk_label_new (NULL);
-        gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
-        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Updated:</b>"));
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        /* Translators: Item name in the status table */
+        gtk_label_set_markup (GTK_LABEL (label), _("Serial Number:"));
         gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
-        label = gdu_time_label_new (NULL);
+        label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->updated_label = label;
+        dialog->priv->serial_label = label;
 
         gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
         row++;
 
-        /* bad sectors */
+        /* ------------------------------ */
+        /* power on hours */
+
+        /* Translators: Tooltip for the "Powered On:" item in the status table */
+        tooltip_markup = _("The amount of elapsed time the disk has been in a powered-up state");
+
         label = gtk_label_new (NULL);
-        gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
-        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Sectors:</b>"));
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        /* Translators: Item name in the status table */
+        gtk_label_set_markup (GTK_LABEL (label), _("Powered On:"));
         gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
         label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->sectors_label = label;
+        dialog->priv->power_on_hours_label = label;
 
         gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
         row++;
 
-        /* attributes */
+        /* ------------------------------ */
+        /* temperature */
+
+        /* Translators: Tooltip for the "Temperature:" item in the status table */
+        tooltip_markup = _("The temperature of the disk");
+
         label = gtk_label_new (NULL);
-        gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
-        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Attributes:</b>"));
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        /* Translators: Item name in the status table */
+        gtk_label_set_markup (GTK_LABEL (label), _("Temperature:"));
         gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
         label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->attributes_label = label;
+        dialog->priv->temperature_label = label;
 
         gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
         row++;
 
-        /* assessment */
+        /* ------------------------------ */
+        /* bad sectors */
+
+        /* Translators: Tooltip for the "Bad Sectors" item in the status table */
+        tooltip_markup = _("The sum of pending and reallocated bad sectors");
+
         label = gtk_label_new (NULL);
-        gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
-        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Assessment:</b>"));
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        /* Translators: Item name in the status table */
+        gtk_label_set_markup (GTK_LABEL (label), _("Bad Sectors:"));
         gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
 
         label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        dialog->priv->sectors_label = label;
+        gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
+                          GTK_FILL, GTK_FILL, 0, 0);
+        row++;
+
+        /* ------------------------------ */
+        /* self assessment */
+
+        /* Translators: Tooltip for the "Self Assessment" item in the status table */
+        tooltip_markup = _("The assessment from the disk itself whether it is about to fail");
+
+        label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-        dialog->priv->assessment_label = label;
+        /* Translators: Item name in the status table */
+        gtk_label_set_markup (GTK_LABEL (label), _("Self Assessment:"));
+        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+                          GTK_FILL, GTK_FILL, 0, 0);
 
+        label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        dialog->priv->self_assessment_label = label;
         gtk_table_attach (GTK_TABLE (table), label, 1, 2, row, row + 1,
                           GTK_FILL, GTK_FILL, 0, 0);
+        row++;
+
+        /* ------------------------------ */
+        /* overall assessment */
+
+        /* Translators: Tooltip for the "Overall Assessment" in the status table */
+        tooltip_markup = _("An overall assessment of the health of the disk");
+
+        label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+        /* Translators: Item name in the status table */
+        gtk_label_set_markup (GTK_LABEL (label), _("Overall Assessment:"));
+        gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
+                          GTK_FILL, GTK_FILL, 0, 0);
+
+        hbox = gtk_hbox_new (FALSE, 2);
+        image = gtk_image_new_from_icon_name ("gdu-smart-unknown",
+                                              GTK_ICON_SIZE_MENU);
+        gtk_widget_set_tooltip_markup (image, tooltip_markup);
+        gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+        dialog->priv->overall_assessment_image = image;
+
+        label = gtk_label_new (NULL);
+        gtk_widget_set_tooltip_markup (label, tooltip_markup);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+        dialog->priv->overall_assessment_label = label;
+        gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
 
+        gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, row, row + 1,
+                          GTK_FILL, GTK_FILL, 0, 0);
         row++;
 
+        /* ------------------------------ */
+
+        /* Translators: Tooltip for the "Do not warn if disk is failing" check button */
+        tooltip_markup = _("Leave unchecked to get notified if the disk starts failing");
+
+        /* Translators: Check button in the status table */
+        check_button = gtk_check_button_new_with_mnemonic (_("Don't _warn me if the disk is failing"));
+        gtk_widget_set_tooltip_markup (check_button, tooltip_markup);
+        gtk_box_pack_start (GTK_BOX (vbox2), check_button, FALSE, FALSE, 0);
+        dialog->priv->no_warn_check_button = check_button;
+        g_signal_connect (check_button,
+                          "toggled",
+                          G_CALLBACK (on_no_warn_check_button_toggled),
+                          dialog);
+
         /* ---------------------------------------------------------------------------------------------------- */
         /* attributes in a tree view */
 
-        dialog->priv->attr_list_store = gtk_list_store_new (ATTR_N_COLUMNS,
-                                                            G_TYPE_STRING,
-                                                            G_TYPE_INT,
-                                                            G_TYPE_STRING,
-                                                            G_TYPE_STRING,
-                                                            G_TYPE_STRING,
-                                                            G_TYPE_STRING,
-                                                            G_TYPE_STRING,
-                                                            G_TYPE_STRING,
-                                                            GDK_TYPE_PIXBUF,
-                                                            G_TYPE_STRING,
-                                                            G_TYPE_STRING,
-                                                            G_TYPE_STRING,
-                                                            G_TYPE_STRING);
+        label = gtk_label_new (NULL);
+        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+        /* Translators: Heading used in the main dialog for SMART attributes*/
+        s = g_strconcat ("<b>", _("_Attributes"), "</b>", NULL);
+        gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), s);
+        g_free (s);
+        gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+        align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+        gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 0);
+        gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0);
+
+        dialog->priv->attr_list_store = gtk_list_store_new (N_COLUMNS,
+                                                            G_TYPE_INT,         /* id */
+                                                            G_TYPE_STRING,      /* name */
+                                                            G_TYPE_STRING,      /* value */
+                                                            G_TYPE_STRING,      /* tooltip */
+                                                            _GDU_TYPE_SK_ATTR); /* SkSmartAttributeParsedData pointer */
 
         tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (dialog->priv->attr_list_store));
+        gtk_label_set_mnemonic_widget (GTK_LABEL (label), tree_view);
         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree_view), TRUE);
-        gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (tree_view), ATTR_TOOLTIP_COLUMN);
+        gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (tree_view), TOOLTIP_COLUMN);
         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->priv->attr_list_store),
-                                              ATTR_ID_INT_COLUMN,
+                                              ID_COLUMN,
                                               GTK_SORT_ASCENDING);
         dialog->priv->tree_view = tree_view;
 
@@ -649,98 +1983,71 @@ gdu_ata_smart_dialog_constructed (GObject *object)
                           dialog);
 
         column = gtk_tree_view_column_new ();
-        gtk_tree_view_column_set_title (column, "ID");
-        renderer = gtk_cell_renderer_text_new ();
-        gtk_tree_view_column_pack_start (column, renderer, TRUE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "text", ATTR_ID_COLUMN,
-                                             NULL);
         gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
-
-        column = gtk_tree_view_column_new ();
-        gtk_tree_view_column_set_title (column, "Attribute");
+        /* Translators: This string is used as the column title in the treeview for the Attribute ID (0-255) */
+        gtk_tree_view_column_set_title (column, _("ID"));
         renderer = gtk_cell_renderer_text_new ();
         gtk_tree_view_column_pack_start (column, renderer, TRUE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "text", ATTR_DESC_COLUMN,
-                                             NULL);
-        gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column),
+                                            renderer,
+                                            format_markup_id,
+                                            dialog,
+                                            NULL);
 
         column = gtk_tree_view_column_new ();
-        gtk_tree_view_column_set_title (column, "Current");
-        renderer = gtk_cell_renderer_text_new ();
-        g_object_set (renderer, "xalign", 1.0, NULL);
-        gtk_tree_view_column_pack_start (column, renderer, TRUE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "text", ATTR_CURRENT_COLUMN,
-                                             NULL);
         gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
-
-        column = gtk_tree_view_column_new ();
-        gtk_tree_view_column_set_title (column, "Worst");
+        /* Translators: This string is used as the column title in the treeview for the attribute name and description */
+        gtk_tree_view_column_set_title (column, _("Attribute"));
+        gtk_tree_view_column_set_expand (column, TRUE);
         renderer = gtk_cell_renderer_text_new ();
-        g_object_set (renderer, "xalign", 1.0, NULL);
+        g_object_set (renderer,
+                      "wrap-width", 300,
+                      "wrap-mode", PANGO_WRAP_WORD_CHAR,
+                      NULL);
         gtk_tree_view_column_pack_start (column, renderer, TRUE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "text", ATTR_WORST_COLUMN,
-                                             NULL);
-        gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
-
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column),
+                                            renderer,
+                                            format_markup_name,
+                                            dialog,
+                                            NULL);
 
         column = gtk_tree_view_column_new ();
-        gtk_tree_view_column_set_title (column, "Threshold");
-        renderer = gtk_cell_renderer_text_new ();
-        g_object_set (renderer, "xalign", 1.0, NULL);
-        gtk_tree_view_column_pack_start (column, renderer, TRUE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "text", ATTR_THRESHOLD_COLUMN,
-                                             NULL);
         gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
-
-        column = gtk_tree_view_column_new ();
-        gtk_tree_view_column_set_title (column, "Value");
-        renderer = gtk_cell_renderer_text_new ();
-        g_object_set (renderer, "xalign", 1.0, NULL);
-        gtk_tree_view_column_pack_start (column, renderer, TRUE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "text", ATTR_VALUE_COLUMN,
-                                             NULL);
-        gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
-
-        column = gtk_tree_view_column_new ();
-        gtk_tree_view_column_set_title (column, "Status");
+        /* Translators: This string is used as the column title in the treeview for the assessment of the attribute */
+        gtk_tree_view_column_set_title (column, _("Assessment"));
         renderer = gtk_cell_renderer_pixbuf_new ();
         gtk_tree_view_column_pack_start (column, renderer, FALSE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "pixbuf", ATTR_STATUS_PIXBUF_COLUMN,
-                                             NULL);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column),
+                                            renderer,
+                                            pixbuf_assessment,
+                                            dialog,
+                                            NULL);
         renderer = gtk_cell_renderer_text_new ();
-        gtk_tree_view_column_pack_start (column, renderer, TRUE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "markup", ATTR_STATUS_TEXT_COLUMN,
-                                             NULL);
-        gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+        gtk_tree_view_column_pack_start (column, renderer, FALSE);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column),
+                                            renderer,
+                                            format_markup_assessment,
+                                            dialog,
+                                            NULL);
 
         column = gtk_tree_view_column_new ();
-        gtk_tree_view_column_set_title (column, "Type");
-        renderer = gtk_cell_renderer_text_new ();
-        g_object_set (renderer, "xalign", 1.0, NULL);
-        gtk_tree_view_column_pack_start (column, renderer, TRUE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "text", ATTR_TYPE_COLUMN,
-                                             NULL);
         gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
-
-        column = gtk_tree_view_column_new ();
-        gtk_tree_view_column_set_title (column, "Updates");
+        /* Translators: This string is used as the column title in the treeview for the value of the attribute */
+        gtk_tree_view_column_set_title (column, _("Value"));
         renderer = gtk_cell_renderer_text_new ();
-        g_object_set (renderer, "xalign", 1.0, NULL);
-        gtk_tree_view_column_pack_start (column, renderer, TRUE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "text", ATTR_UPDATES_COLUMN,
-                                             NULL);
-        gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
-
+        gtk_tree_view_column_pack_start (column, renderer, FALSE);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column),
+                                            renderer,
+                                            format_markup_value_headings,
+                                            dialog,
+                                            NULL);
+        renderer = gtk_cell_renderer_text_new ();
+        gtk_tree_view_column_pack_start (column, renderer, FALSE);
+        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column),
+                                            renderer,
+                                            format_markup_value,
+                                            dialog,
+                                            NULL);
 
         scrolled_window = gtk_scrolled_window_new (NULL, NULL);
         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
@@ -749,39 +2056,17 @@ gdu_ata_smart_dialog_constructed (GObject *object)
                                              GTK_SHADOW_IN);
         gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
 
-	gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+        gtk_container_add (GTK_CONTAINER (align), scrolled_window);
 
-        gtk_window_set_default_size (GTK_WINDOW (dialog), 500, 500);
+        /* ---------------------------------------------------------------------------------------------------- */
 
-        update_dialog (dialog);
+        gtk_window_set_default_size (GTK_WINDOW (dialog), 700, 600);
 
-        dialog->priv->device_changed_signal_handler_id = g_signal_connect (dialog->priv->device,
-                                                                           "changed",
-                                                                           G_CALLBACK (device_changed),
-                                                                           dialog);
-
-        GtkWidget *scale;
-        scale = gtk_hscale_new_with_range (2.0,
-                                           365 * 24.0,
-                                           1.0);
-        gtk_scale_set_digits (GTK_SCALE (scale), 0);
-        gtk_scale_set_draw_value (GTK_SCALE (scale), TRUE);
-	gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
-        g_signal_connect (scale,
-                          "value-changed",
-                          G_CALLBACK (on_period_scale_changed),
-                          dialog);
+        dialog->priv->has_been_constructed = TRUE;
+        update_dialog (dialog);
 
-        scale = gtk_hscale_new_with_range (0.0,
-                                           365 * 24.0,
-                                           1.0);
-        gtk_scale_set_digits (GTK_SCALE (scale), 0);
-        gtk_scale_set_draw_value (GTK_SCALE (scale), TRUE);
-	gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
-        g_signal_connect (scale,
-                          "value-changed",
-                          G_CALLBACK (on_end_scale_changed),
-                          dialog);
+        /* Focus the first attribute view */
+        gtk_widget_grab_focus (dialog->priv->tree_view);
 
         if (G_OBJECT_CLASS (gdu_ata_smart_dialog_parent_class)->constructed != NULL)
                 G_OBJECT_CLASS (gdu_ata_smart_dialog_parent_class)->constructed (object);
@@ -800,14 +2085,14 @@ gdu_ata_smart_dialog_class_init (GduAtaSmartDialogClass *klass)
         object_class->finalize     = gdu_ata_smart_dialog_finalize;
 
         g_object_class_install_property (object_class,
-                                         PROP_DEVICE,
-                                         g_param_spec_object ("device",
-                                                              _("Device"),
-                                                              _("The device to show ATA SMART data for"),
-                                                              GDU_TYPE_DEVICE,
+                                         PROP_DRIVE,
+                                         g_param_spec_object ("drive",
+                                                              NULL,
+                                                              NULL,
+                                                              GDU_TYPE_DRIVE,
                                                               G_PARAM_READABLE |
                                                               G_PARAM_WRITABLE |
-                                                              G_PARAM_CONSTRUCT_ONLY));
+                                                              G_PARAM_CONSTRUCT));
 }
 
 static void
@@ -818,20 +2103,19 @@ gdu_ata_smart_dialog_init (GduAtaSmartDialog *dialog)
 
 GtkWidget *
 gdu_ata_smart_dialog_new (GtkWindow *parent,
-                          GduDevice *device)
+                          GduDrive  *drive)
 {
         return GTK_WIDGET (g_object_new (GDU_TYPE_ATA_SMART_DIALOG,
                                          "transient-for", parent,
-                                         "device", device,
+                                         "drive", drive,
                                          NULL));
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
 static gchar *
-pretty_to_string (guint64                  pretty_value,
-                  GduAtaSmartAttributeUnit pretty_unit,
-                  gboolean                 long_string)
+pretty_to_string (uint64_t              pretty_value,
+                  SkSmartAttributeUnit  pretty_unit)
 {
         gchar *ret;
         gdouble celcius;
@@ -839,63 +2123,62 @@ pretty_to_string (guint64                  pretty_value,
 
         switch (pretty_unit) {
 
-        case GDU_ATA_SMART_ATTRIBUTE_UNIT_MSECONDS:
-                if (long_string) {
-                        if (pretty_value > 1000 * 60 * 60 * 24) {
-                                ret = g_strdup_printf (_("%.3f days"), pretty_value / 1000.0 / 60.0 / 60.0 / 24.0);
-                        } else if (pretty_value > 1000 * 60 * 60) {
-                                ret = g_strdup_printf (_("%.3f hours"), pretty_value / 1000.0 / 60.0 / 60.0);
-                        } else if (pretty_value > 1000 * 60) {
-                                ret = g_strdup_printf (_("%.3f minutes"), pretty_value / 1000.0 / 60.0);
-                        } else if (pretty_value > 1000) {
-                                ret = g_strdup_printf (_("%.3f seconds"), pretty_value / 1000.0);
-                        } else {
-                                ret = g_strdup_printf (_("%" G_GUINT64_FORMAT " msec"), pretty_value);
-                        }
+        case SK_SMART_ATTRIBUTE_UNIT_MSECONDS:
+                if (pretty_value > 1000 * 60 * 60 * 24 * 365.25) {
+                        /* Translators: Used in the treeview for the pretty/interpreted value of an attribute
+                         * for a time-based unit that exceed one year */
+                        ret = g_strdup_printf (_("%.1f years"), pretty_value / 1000.0 / 60.0 / 60.0 / 24.0 / 365.25);
+                } else if (pretty_value > 1000 * 60 * 60 * 24) {
+                        /* Translators: Used in the treeview for the pretty/interpreted value of an attribute
+                         * for a time-based unit that exceed one day */
+                        ret = g_strdup_printf (_("%.1f days"), pretty_value / 1000.0 / 60.0 / 60.0 / 24.0);
+                } else if (pretty_value > 1000 * 60 * 60) {
+                        /* Translators: Used in the treeview for the pretty/interpreted value of an attribute
+                         * for a time-based unit that exceed one hour */
+                        ret = g_strdup_printf (_("%.1f hours"), pretty_value / 1000.0 / 60.0 / 60.0);
+                } else if (pretty_value > 1000 * 60) {
+                        /* Translators: Used in the treeview for the pretty/interpreted value of an attribute
+                         * for a time-based unit that exceed one minute */
+                        ret = g_strdup_printf (_("%.1f minutes"), pretty_value / 1000.0 / 60.0);
+                } else if (pretty_value > 1000) {
+                        /* Translators: Used in the treeview for the pretty/interpreted value of an attribute
+                         * for a time-based unit that exceed one second */
+                        ret = g_strdup_printf (_("%.1f seconds"), pretty_value / 1000.0);
                 } else {
-                        if (pretty_value > 1000 * 60 * 60 * 24) {
-                                ret = g_strdup_printf (_("%.0f d"), pretty_value / 1000.0 / 60.0 / 60.0 / 24.0);
-                        } else if (pretty_value > 1000 * 60 * 60) {
-                                ret = g_strdup_printf (_("%.0f h"), pretty_value / 1000.0 / 60.0 / 60.0);
-                        } else if (pretty_value > 1000 * 60) {
-                                ret = g_strdup_printf (_("%.0f m"), pretty_value / 1000.0 / 60.0);
-                        } else if (pretty_value > 1000) {
-                                ret = g_strdup_printf (_("%.0f s"), pretty_value / 1000.0);
-                        } else {
-                                ret = g_strdup_printf (_("%" G_GUINT64_FORMAT " msec"), pretty_value);
-                        }
+                        /* Translators: Used in the treeview for the pretty/interpreted value of an attribute
+                         * for a time-based unit that is counted in milliseconds */
+                        ret = g_strdup_printf (_("%" G_GUINT64_FORMAT " msec"), pretty_value);
                 }
                 break;
 
-        case GDU_ATA_SMART_ATTRIBUTE_UNIT_SECTORS:
-                if (long_string) {
-                        if (pretty_value == 1)
-                                ret = g_strdup (_("1 Sector"));
-                        else
-                                ret = g_strdup_printf (_("%" G_GUINT64_FORMAT " Sectors"), pretty_value);
-                } else {
-                        ret = g_strdup_printf (_("%" G_GUINT64_FORMAT), pretty_value);
-                }
+        case SK_SMART_ATTRIBUTE_UNIT_SECTORS:
+                /* Translators: Used in the treeview for the pretty/interpreted value of an attribute
+                 * for a sector-based unit */
+                ret = g_strdup_printf (dngettext (GETTEXT_PACKAGE,
+                                                  "%d sector",
+                                                  "%d sectors",
+                                                  (gint) pretty_value),
+                                       (gint) pretty_value);
                 break;
 
-        case GDU_ATA_SMART_ATTRIBUTE_UNIT_MKELVIN:
-                if (long_string) {
-                        celcius = pretty_value / 1000.0 - 273.15;
-                        fahrenheit = 9.0 * celcius / 5.0 + 32.0;
-                        ret = g_strdup_printf (_("%.3f\302\260 C / %.3f\302\260 F"), celcius, fahrenheit);
-                } else {
-                        /* We could choose kelvin here to treat C and F camps equally. But
-                         * that would be lame.
-                         */
-                        celcius = pretty_value / 1000.0 - 273.15;
-                        ret = g_strdup_printf (_("%.0f\302\260 C"), celcius);
-                }
+        case SK_SMART_ATTRIBUTE_UNIT_MKELVIN:
+                celcius = pretty_value / 1000.0 - 273.15;
+                fahrenheit = 9.0 * celcius / 5.0 + 32.0;
+                /* Translators: Used in the treeview for the pretty/interpreted value of an attribute
+                 * for a temperature-based unit - first %f is the temperature in degrees Celcius, second %f
+                 * is the temperature in degrees Fahrenheit */
+                ret = g_strdup_printf (_("%.0f° C / %.0f° F"), celcius, fahrenheit);
+                break;
+
+        case SK_SMART_ATTRIBUTE_UNIT_NONE:
+                ret = g_strdup_printf ("%" G_GUINT64_FORMAT, pretty_value);
                 break;
 
         default:
-        case GDU_ATA_SMART_ATTRIBUTE_UNIT_NONE:
-        case GDU_ATA_SMART_ATTRIBUTE_UNIT_UNKNOWN:
-                ret = g_strdup_printf (_("%" G_GUINT64_FORMAT), pretty_value);
+        case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN:
+                /* Translators: Used in the treeview for the pretty/interpreted value of an attribute
+                 * where the value cannot be interpreted */
+                ret = g_strdup (_("N/A"));
                 break;
         }
 
@@ -904,318 +2187,606 @@ pretty_to_string (guint64                  pretty_value,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static gboolean
+find_row_with_name (GtkListStore *list_store,
+                    const gchar *name,
+                    GtkTreeIter *out_iter)
+{
+        GtkTreeIter iter;
+        gboolean ret;
+
+        ret = FALSE;
+
+        if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter)) {
+                do {
+                        gchar *row_name;
+
+                        gtk_tree_model_get (GTK_TREE_MODEL (list_store),
+                                            &iter,
+                                            NAME_COLUMN, &row_name,
+                                            -1);
+                        if (g_strcmp0 (name, row_name) == 0) {
+                                g_free (row_name);
+                                ret = TRUE;
+                                if (out_iter != NULL)
+                                        *out_iter = iter;
+                                goto out;
+                        }
+                        g_free (row_name);
+                } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), &iter));
+        }
+
+ out:
+        return ret;
+}
+
 static void
-get_historical_data_cb (GduDevice *device,
-                        GList     *smart_data,
-                        GError    *error,
-                        gpointer   user_data)
+attr_foreach_add_cb (SkDisk                           *d,
+                     const SkSmartAttributeParsedData *a,
+                     void                             *user_data)
 {
         GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
+        GtkTreeIter iter;
+        gchar *pretty_str;
+        gchar *tooltip_str;
+        const gchar *tip_type_str;
+        const gchar *tip_updates_str;
+
+        if (a->prefailure) {
+                /* Translators: Used in the tooltip for a row in the attribute treeview - please keep
+                 * "(Pre-Fail)" in English */
+                tip_type_str = _("Failure is a sign of imminent disk failure (Pre-Fail)");
+        } else {
+                /* Translators: Used in the tooltip for a row in the attribute treeview - please keep
+                 * "(Old-Age)" in English */
+                tip_type_str = _("Failure is a sign of old age (Old-Age)");
+        }
 
-        if (error != NULL) {
-                g_warning ("Error getting historical data: %s", error->message);
-                g_error_free (error);
+        if (a->online) {
+                /* Translators: Used in the tooltip for a row in the attribute treeview - please keep
+                 * "(Online)" in English */
+                tip_updates_str = _("Every time data is collected (Online)");
         } else {
-                if (dialog->priv->historical_data != NULL) {
-                        g_list_foreach (dialog->priv->historical_data, (GFunc) g_object_unref, NULL);
-                        g_list_free (dialog->priv->historical_data);
-                }
-                dialog->priv->historical_data = smart_data;
+                /* Translators: Used in the tooltip for a row in the attribute treeview - please keep
+                 * "(Not Online)" in English */
+                tip_updates_str = _("Only during off-line activities (Not Online)");
+        }
 
-                g_debug ("got historical data (%d elems)", g_list_length (smart_data));
+        /* Translators: Used in the tooltip for a row in the attribute treeview.
+         * First %s is the type of the attribute (Pre-Fail or Old-Age).
+         * Second %s is the update type (Online or Not Online).
+         * The six %x is the raw data of the attribute.
+         */
+        tooltip_str = g_strdup_printf (_("Type: %s\n"
+                                         "Updates: %s\n"
+                                         "Raw: 0x%02x%02x%02x%02x%02x%02x"),
+                                       tip_type_str,
+                                       tip_updates_str,
+                                       a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5]);
+
+        pretty_str = pretty_to_string (a->pretty_value, a->pretty_unit);
+
+        gtk_list_store_append (dialog->priv->attr_list_store, &iter);
+        gtk_list_store_set (dialog->priv->attr_list_store, &iter,
+                            ID_COLUMN, a->id,
+                            NAME_COLUMN, a->name,
+                            VALUE_COLUMN, pretty_str,
+                            TOOLTIP_COLUMN, tooltip_str,
+                            SK_ATTR_COLUMN, a,
+                            -1);
+        g_free (pretty_str);
+        g_free (tooltip_str);
+}
 
-                update_dialog (dialog);
+static gboolean
+is_self_test_running (GduDevice *device,
+                      SkSmartSelfTest *out_test_type)
+{
+        gboolean ret;
+
+        ret = FALSE;
+
+        if (!gdu_device_job_in_progress (device))
+                goto out;
+
+        if (g_strcmp0 (gdu_device_job_get_id (device), "DriveAtaSmartSelftestShort") == 0) {
+                ret = TRUE;
+                if (out_test_type != NULL)
+                        *out_test_type = SK_SMART_SELF_TEST_SHORT;
+        } else if (g_strcmp0 (gdu_device_job_get_id (device), "DriveAtaSmartSelftestExtended") == 0) {
+                ret = TRUE;
+                if (out_test_type != NULL)
+                        *out_test_type = SK_SMART_SELF_TEST_EXTENDED;
+        } else if (g_strcmp0 (gdu_device_job_get_id (device), "DriveAtaSmartSelftestConveyance") == 0) {
+                ret = TRUE;
+                if (out_test_type != NULL)
+                        *out_test_type = SK_SMART_SELF_TEST_CONVEYANCE;
+        }
+
+ out:
+        return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* TODO: in the future we probably want to get/set this via the daemon - e.g. so the preference also
+ * takes effect system-wide */
+
+/* TODO: keep in sync with src/notification/notification-main.c */
+static gboolean
+get_ata_smart_no_warn (GduDevice *device)
+{
+        gboolean ret;
+        gchar *path;
+        gchar *disk_id;
+        struct stat stat_buf;
+
+        ret = FALSE;
+
+        disk_id = g_strdup_printf ("%s-%s-%s-%s",
+                                   gdu_device_drive_get_vendor (device),
+                                   gdu_device_drive_get_model (device),
+                                   gdu_device_drive_get_revision (device),
+                                   gdu_device_drive_get_serial (device));
+
+        path = g_build_filename (g_get_user_config_dir (),
+                                 "gnome-disk-utility",
+                                 "ata-smart-ignore",
+                                 disk_id,
+                                 NULL);
+
+        if (g_stat (path, &stat_buf) == 0) {
+                ret = TRUE;
         }
 
-        g_object_unref (dialog);
+        g_free (path);
+        g_free (disk_id);
+
+        return ret;
 }
 
+static gboolean
+set_ata_smart_no_warn (GduDevice *device,
+                       gboolean   no_warn)
+{
+        gboolean ret;
+        gchar *path;
+        gchar *dir_path;
+        gchar *disk_id;
+        gint fd;
+
+        disk_id = NULL;
+        dir_path = NULL;
+        path = NULL;
+        ret = FALSE;
+
+        disk_id = g_strdup_printf ("%s-%s-%s-%s",
+                                   gdu_device_drive_get_vendor (device),
+                                   gdu_device_drive_get_model (device),
+                                   gdu_device_drive_get_revision (device),
+                                   gdu_device_drive_get_serial (device));
+
+        dir_path = g_build_filename (g_get_user_config_dir (),
+                                     "gnome-disk-utility",
+                                     "ata-smart-ignore",
+                                     NULL);
+
+        path = g_build_filename (dir_path,
+                                 disk_id,
+                                 NULL);
+
+        if (g_mkdir_with_parents (dir_path, 0755) != 0) {
+                g_warning ("Error creating directory `%s': %s",
+                           dir_path,
+                           g_strerror (errno));
+                goto out;
+        }
+
+        if (no_warn) {
+                fd = g_creat (path, 0644);
+                if (fd == -1) {
+                        g_warning ("Error creating file `%s': %s",
+                                   path,
+                                   g_strerror (errno));
+                }
+                close (fd);
+        } else {
+                if (g_unlink (path) != 0) {
+                        g_warning ("Error unlinking `%s': %s",
+                                   path,
+                                   g_strerror (errno));
+                        goto out;
+                }
+        }
+
+        ret = TRUE;
+
+ out:
+        g_free (path);
+        g_free (dir_path);
+        g_free (disk_id);
+
+        return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
 
 static void
 update_dialog (GduAtaSmartDialog *dialog)
 {
-        gchar *assessment_text;
+        gchar *self_assessment_text;
+        gchar *overall_assessment_text;
         gchar *bad_sectors_text;
-        gchar *attributes_text;
         gchar *powered_on_text;
+        gchar *model_text;
+        gchar *firmware_text;
+        gchar *serial_text;
         gchar *temperature_text;
         gchar *selftest_text;
-        guint64 temperature_mkelvin;
-        guint64 power_on_msec;
+        gchar *action_text;
+        gboolean highlight;
         GTimeVal updated;
-        gboolean is_failing;
-        gboolean is_failing_valid;
-        gboolean has_bad_sectors;
-        gboolean has_bad_attributes;
-        GduAtaSmartSelfTestExecutionStatus self_test_status;
-        GList *attributes;
-        GList *l;
-
-        if (!gdu_device_drive_ata_smart_get_is_available (dialog->priv->device)) {
-                assessment_text = g_strdup (_("ATA SMART Not Supported"));
-                bad_sectors_text = g_strdup ("-");
-                attributes_text = g_strdup ("-");
-                powered_on_text = g_strdup ("-");
-                temperature_text = g_strdup ("-");
-                selftest_text = g_strdup ("-");
-
-                updated.tv_sec = 0;
-
-                attributes = NULL;
-
-                //polkit_gnome_action_set_sensitive (dialog->priv->refresh_action, FALSE);
-                //polkit_gnome_action_set_sensitive (dialog->priv->details_action, FALSE);
-                //polkit_gnome_action_set_sensitive (dialog->priv->selftest_action, FALSE);
-                dialog->priv->last_updated = 0;
+        gconstpointer blob;
+        gsize blob_size;
+        GIcon *status_icon;
+        gchar *s;
+        SkBool self_assessment_good;
+        uint64_t num_bad_sectors;
+        uint64_t power_on_msec;
+        uint64_t temperature_mkelvin;
+        SkSmartSelfTest test_type;
+        SkDisk *sk_disk;
+        const SkSmartParsedData *parsed_data;
+        const SkIdentifyParsedData *parsed_identify_data;
+        gboolean no_warn;
+
+        self_assessment_text = NULL;
+        overall_assessment_text = NULL;
+        bad_sectors_text = NULL;
+        model_text = NULL;
+        firmware_text = NULL;
+        serial_text = NULL;
+        powered_on_text = NULL;
+        temperature_text = NULL;
+        selftest_text = NULL;
+        action_text = NULL;
+        updated.tv_sec = 0;
+        dialog->priv->last_updated = 0;
+        status_icon = NULL;
+        sk_disk = NULL;
+
+        /* avoid updating anything if the widgets hasn't been constsructed */
+        if (!dialog->priv->has_been_constructed)
                 goto out;
-        }
 
-        attributes = gdu_device_drive_ata_smart_get_attributes (dialog->priv->device);
+        if (dialog->priv->device == NULL) {
+                /* Translators: Shown in the "Overall Assessment" item in the status table
+                 * when no drive is currently selected */
+                overall_assessment_text = g_strdup (_("No drive selected"));
+                goto has_data;
+        }
 
-        is_failing = gdu_device_drive_ata_smart_get_is_failing (dialog->priv->device);
-        is_failing_valid = gdu_device_drive_ata_smart_get_is_failing_valid (dialog->priv->device);
+        if (!gdu_device_drive_ata_smart_get_is_available (dialog->priv->device)) {
+                /* Translators: Shown in the "Overall Assessment" item in the status table
+                 * when SMART is not available */
+                overall_assessment_text = g_strdup (_("SMART not supported"));
+                goto has_data;
+        }
 
-        self_test_status = gdu_device_drive_ata_smart_get_self_test_execution_status (dialog->priv->device);
+        blob = gdu_device_drive_ata_smart_get_blob (dialog->priv->device, &blob_size);
+        if (blob == NULL) {
+                /* Translators: Shown in the "Overall Assessment" item in the status table
+                 * when SMART is supported but data was never collected */
+                overall_assessment_text = g_strdup (_("SMART data never collected"));
+                goto has_data;
+        }
 
-        power_on_msec = 1000 * gdu_device_drive_ata_smart_get_power_on_seconds (dialog->priv->device);
-        temperature_mkelvin = (guint64) (gdu_device_drive_ata_smart_get_temperature_kelvin (dialog->priv->device) * 1000.0);
+        if (sk_disk_open (NULL, &sk_disk) != 0) {
+                /* Translators: Shown in the "Overall Assessment" item in the status table
+                 * when the SMART data is malformed */
+                overall_assessment_text = g_strdup (_("SMART data is malformed"));
+                goto has_data;
+        }
+        if (sk_disk_set_blob (sk_disk, blob, blob_size) != 0) {
+                /* Translators: Shown in the "Overall Assessment" item in the status table
+                 * when the SMART data is malformed */
+                overall_assessment_text = g_strdup (_("SMART data is malformed"));
+                goto has_data;
+        }
 
-        has_bad_sectors = gdu_device_drive_ata_smart_get_has_bad_sectors (dialog->priv->device);
-        has_bad_attributes = gdu_device_drive_ata_smart_get_has_bad_attributes (dialog->priv->device);
+        dialog->priv->last_updated = updated.tv_sec = gdu_device_drive_ata_smart_get_time_collected (dialog->priv->device);
+        updated.tv_usec = 0;
 
-        //polkit_gnome_action_set_sensitive (dialog->priv->refresh_action, TRUE);
-        //polkit_gnome_action_set_sensitive (dialog->priv->details_action, TRUE);
-        //polkit_gnome_action_set_sensitive (dialog->priv->selftest_action, TRUE);
+        s = gdu_util_ata_smart_status_to_desc (gdu_device_drive_ata_smart_get_status (dialog->priv->device),
+                                               &highlight,
+                                               &action_text,
+                                               &status_icon);
+        if (highlight) {
+                gchar *s2;
+                s2 = g_strdup_printf ("<span fgcolor=\"red\"><b>%s</b></span>", s);
+                g_free (s);
+                s = s2;
+        }
+        if (action_text != NULL) {
+                overall_assessment_text = g_strdup_printf ("%s\n"
+                                                           "<small>%s</small>",
+                                                           s,
+                                                           action_text);
+                g_free (action_text);
+        } else {
+                overall_assessment_text = s;
+                s = NULL;
+        }
 
-        if (is_failing_valid) {
-                if (!is_failing) {
-                        assessment_text = g_strdup (_("Passed"));
+        if (sk_disk_smart_status (sk_disk, &self_assessment_good) != 0) {
+                /* Translators: Shown in the "Self-assessment" item in the status table
+                 * when the self-assessment of the drive is unknown */
+                self_assessment_text = g_strdup (_("Unknown"));
+        } else {
+                if (self_assessment_good) {
+                        /* Translators: Shown in the "Self-assessment" item in the status table
+                         * when the self-assessment of the drive is PASSED */
+                        self_assessment_text = g_strdup (_("Passed"));
                 } else {
-                        assessment_text = g_strdup (_("<span foreground='red'><b>FAILING</b></span>"));
+                        self_assessment_text = g_strdup_printf ("<span foreground='red'><b>%s</b></span>",
+                                                                /* Translators: Shown in the "Self-assessment" item in
+                                                                 * the status table when the self-assessment of the
+                                                                 * drive is FAILING */
+                                                                _("FAILING"));
                 }
-        } else {
-                assessment_text = g_strdup (_("Unknown"));
         }
 
-        if (has_bad_sectors)
-                bad_sectors_text = g_strdup (_("<span foreground='red'><b>BAD SECTORS DETECTED</b></span>"));
-        else
-                bad_sectors_text = g_strdup (_("No bad sectors detected"));
-
-        if (has_bad_attributes)
-                attributes_text = g_strdup (_("<span foreground='red'><b>EXCEEDS THRESHOLD</b></span>"));
-        else
-                attributes_text = g_strdup (_("Within threshold"));
+        if (sk_disk_smart_get_bad (sk_disk, &num_bad_sectors) != 0) {
+                /* Translators: Shown in the "Bad Sectors" item in the status table
+                 * when we don't know if the disk has bad sectors */
+                bad_sectors_text = g_strdup (_("Unknown"));
+        } else {
+                if (num_bad_sectors == 0) {
+                        /* Translators: Shown in the "Bad Sectors" item in the status table
+                         * when we the disk has no bad sectors */
+                        bad_sectors_text = g_strdup (_("None"));
+                } else {
+                        /* Translators: Shown in the "Bad Sectors" item in the status table
+                         * when we the disk has one or more bad sectors */
+                        bad_sectors_text = g_strdup_printf (dngettext (GETTEXT_PACKAGE,
+                                                                       "%d bad sector",
+                                                                       "%d bad sectors",
+                                                                       (gint) num_bad_sectors),
+                                                            (gint) num_bad_sectors);
+                }
+        }
 
-        if (power_on_msec == 0) {
+        if (sk_disk_smart_get_power_on (sk_disk, &power_on_msec) != 0) {
+                /* Translators: Shown in the "Powered On" item in the status table when we don't know
+                 * the amount of time the disk has been powered on */
                 powered_on_text = g_strdup (_("Unknown"));
         } else {
-                powered_on_text = pretty_to_string (power_on_msec, GDU_ATA_SMART_ATTRIBUTE_UNIT_MSECONDS, TRUE);
+                powered_on_text = pretty_to_string (power_on_msec, SK_SMART_ATTRIBUTE_UNIT_MSECONDS);
         }
 
-        if (temperature_mkelvin == 0) {
+        if (sk_disk_smart_get_temperature (sk_disk, &temperature_mkelvin) != 0) {
+                /* Translators: Shown in the "Temperature" item in the status table when we don't know
+                 * the temperature of the disk
+                 */
                 temperature_text = g_strdup (_("Unknown"));
         } else {
-                temperature_text = pretty_to_string (temperature_mkelvin, GDU_ATA_SMART_ATTRIBUTE_UNIT_MKELVIN, TRUE);
+                temperature_text = pretty_to_string (temperature_mkelvin, SK_SMART_ATTRIBUTE_UNIT_MKELVIN);
         }
 
-        dialog->priv->last_updated = updated.tv_sec = gdu_device_drive_ata_smart_get_time_collected (dialog->priv->device);
-        updated.tv_usec = 0;
+        if (sk_disk_identify_parse (sk_disk, &parsed_identify_data) == 0) {
+                model_text = g_strdup (parsed_identify_data->model);
+                firmware_text = g_strdup (parsed_identify_data->firmware);
+                serial_text = g_strdup (parsed_identify_data->serial);
+        }
 
-        switch (self_test_status) {
-        case GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER:
-                selftest_text = g_strdup (_("Completed OK"));
-                break;
-        case GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED:
-                selftest_text = g_strdup (_("Cancelled"));
-                break;
-        case GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED:
-                selftest_text = g_strdup (_("Cancelled (with hard or soft reset)"));
-                break;
-        case GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_FATAL:
-                selftest_text = g_strdup (_("Not completed (a fatal error might have occured)"));
-                break;
-        case GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL:
-                selftest_text = g_strdup (_("<span foreground='red'><b>FAILED</b></span> (Electrical)"));
-                break;
-        case GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO:
-                selftest_text = g_strdup (_("<span foreground='red'><b>FAILED</b></span> (Servo)"));
-                break;
-        case GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ:
-                selftest_text = g_strdup (_("<span foreground='red'><b>FAILED</b></span> (Read)"));
-                break;
-        case GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING:
-                selftest_text = g_strdup (_("<span foreground='red'><b>FAILED</b></span> (Suspected of having handled damage"));
-                break;
-        case GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS:
-                selftest_text = g_strdup (_("In progress"));
-                break;
+        if (sk_disk_smart_parse (sk_disk, &parsed_data) == 0) {
+                const gchar *self_text;
+                gboolean highlight;
+
+                highlight = FALSE;
+                switch (parsed_data->self_test_execution_status) {
+                case SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER:
+                        /* Translators: Shown in the "Self-tests" item in the status table */
+                        self_text = _("Last self-test completed OK");
+                        break;
+                case SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED:
+                        /* Translators: Shown in the "Self-tests" item in the status table */
+                        self_text = _("Last self-test was cancelled");
+                        break;
+                case SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED:
+                        /* Translators: Shown in the "Self-tests" item in the status table */
+                        self_text = _("Last self-test was cancelled (with hard or soft reset)");
+                        break;
+                case SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL:
+                        /* Translators: Shown in the "Self-tests" item in the status table */
+                        self_text = _("Last self-test not completed (a fatal error might have occured)");
+                        break;
+                case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL:
+                        /* Translators: Shown in the "Self-tests" item in the status table */
+                        self_text = _("Last self-test FAILED (Electrical)");
+                        highlight = TRUE;
+                        break;
+                case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO:
+                        /* Translators: Shown in the "Self-tests" item in the status table */
+                        self_text = _("Last self-test FAILED (Servo)");
+                        highlight = TRUE;
+                        break;
+                case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ:
+                        /* Translators: Shown in the "Self-tests" item in the status table */
+                        self_text = _("Last self-test FAILED (Read)");
+                        highlight = TRUE;
+                        break;
+                case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING:
+                        /* Translators: Shown in the "Self-tests" item in the status table */
+                        self_text = _("Last self-test FAILED (Suspected of having handled damage)");
+                        highlight = TRUE;
+                        break;
+                case SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS:
+                        /* Translators: Shown in the "Self-tests" item in the status table */
+                        self_text = _("Self-test is in progress");
+                        highlight = TRUE;
+                        break;
+
+                default:
+                case SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN:
+                        /* Translators: Shown in the "Self-tests" item in the status table */
+                        self_text = _("Unknown");
+                        break;
+                }
 
-        default:
-        case GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN:
-                selftest_text = g_strdup (_("Unknown"));
-                break;
+                if (highlight)
+                        selftest_text = g_strdup_printf ("<span foreground='red'><b>%s</b></span>", self_text);
+                else
+                        selftest_text = g_strdup (self_text);
         }
 
- out:
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->assessment_label), assessment_text);
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->sectors_label), bad_sectors_text);
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->attributes_label), attributes_text);
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->power_on_hours_label), powered_on_text);
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->temperature_label), temperature_text);
+ has_data:
+
+        if (status_icon == NULL)
+                status_icon = g_themed_icon_new ("gdu-smart-unknown");
+        gtk_image_set_from_gicon (GTK_IMAGE (dialog->priv->overall_assessment_image),
+                                  status_icon,
+                                  GTK_ICON_SIZE_MENU);
+
+        gtk_label_set_markup (GTK_LABEL (dialog->priv->self_assessment_label), self_assessment_text != NULL ? self_assessment_text : "-");
+        gtk_label_set_markup (GTK_LABEL (dialog->priv->overall_assessment_label), overall_assessment_text != NULL ? overall_assessment_text : "-");
+        gtk_label_set_markup (GTK_LABEL (dialog->priv->sectors_label), bad_sectors_text != NULL ? bad_sectors_text : "-");
+        gtk_label_set_markup (GTK_LABEL (dialog->priv->power_on_hours_label), powered_on_text != NULL ? powered_on_text : "-");
+        gtk_label_set_markup (GTK_LABEL (dialog->priv->temperature_label), temperature_text != NULL ? temperature_text : "-");
+        gtk_label_set_markup (GTK_LABEL (dialog->priv->model_label), model_text != NULL ? model_text : "-");
+        gtk_label_set_markup (GTK_LABEL (dialog->priv->firmware_label), firmware_text != NULL ? firmware_text : "-");
+        gtk_label_set_markup (GTK_LABEL (dialog->priv->serial_label), serial_text != NULL ? serial_text : "-");
         if (updated.tv_sec == 0) {
                 gdu_time_label_set_time (GDU_TIME_LABEL (dialog->priv->updated_label), NULL);
                 gtk_label_set_markup (GTK_LABEL (dialog->priv->updated_label), "-");
         } else {
                 gdu_time_label_set_time (GDU_TIME_LABEL (dialog->priv->updated_label), &updated);
         }
-        gtk_label_set_markup (GTK_LABEL (dialog->priv->last_self_test_result_label), selftest_text);
+        gtk_label_set_markup (GTK_LABEL (dialog->priv->self_test_result_label), selftest_text != NULL ? selftest_text : "-");
 
-        gtk_list_store_clear (dialog->priv->attr_list_store);
-        for (l = attributes; l != NULL; l = l->next) {
-                GduAtaSmartAttribute *a = GDU_ATA_SMART_ATTRIBUTE (l->data);
+        if (sk_disk == NULL) {
+                gtk_list_store_clear (dialog->priv->attr_list_store);
+        } else {
                 GtkTreeIter iter;
-                char *col_str;
-                char *name_str;
-                char *current_str;
-                char *worst_str;
-                char *threshold_str;
-                char *pretty_str;
-                char *status_str;
-                GdkPixbuf *status_pixbuf;
-                char *tooltip_str;
-                int icon_width, icon_height;
-                char *desc_str;
-                const gchar *type_str;
-                const gchar *updates_str;
-                const gchar *tip_type_str;
-                const gchar *tip_updates_str;
-                gboolean is_good;
-                gboolean is_good_valid;
-                guint64 pretty_value;
-                GduAtaSmartAttributeUnit pretty_unit;
-
-                col_str = g_strdup_printf ("%d", gdu_ata_smart_attribute_get_id (a));
-
-                name_str = gdu_ata_smart_attribute_get_localized_name (a);
-                desc_str = gdu_ata_smart_attribute_get_localized_description (a);
-
-                if (desc_str == NULL) {
-                        desc_str = g_strdup_printf (_("No description for attribute %d."),
-                                                    gdu_ata_smart_attribute_get_id (a));
+                gchar *name_selected;
+
+                /* keep selected row if it exists after the refresh (disk may have changed) */
+                name_selected = NULL;
+                if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->tree_view)),
+                                                     NULL,
+                                                     &iter)) {
+                        gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->attr_list_store),
+                                            &iter,
+                                            NAME_COLUMN, &name_selected,
+                                            -1);
                 }
 
-                if (gdu_ata_smart_attribute_get_flags (a) & 0x0001) {
-                        tip_type_str = _("Failure is a sign of imminent disk failure.");
+                gtk_list_store_clear (dialog->priv->attr_list_store);
+                sk_disk_smart_parse_attributes (sk_disk,
+                                                attr_foreach_add_cb,
+                                                dialog);
+
+                if (name_selected != NULL) {
+                        if (!find_row_with_name (dialog->priv->attr_list_store, name_selected, &iter))
+                                gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dialog->priv->attr_list_store), &iter);
+                        g_free (name_selected);
                 } else {
-                        tip_type_str = _("Failure is a sign of old age.");
+                        gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dialog->priv->attr_list_store), &iter);
                 }
+                gtk_tree_selection_select_iter (gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->tree_view)),
+                                                &iter);
+        }
 
-                if (gdu_ata_smart_attribute_get_flags (a) & 0x0002) {
-                        tip_updates_str = _("Every time data is collected.");
+        /* update "no warning" check button */
+        no_warn = FALSE;
+        if (dialog->priv->device != NULL)
+                no_warn = get_ata_smart_no_warn (dialog->priv->device);
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->priv->no_warn_check_button), no_warn);
+
+        /* control visility of update widgets */
+        if (dialog->priv->is_updating) {
+                gtk_widget_hide (dialog->priv->updated_label);
+                gtk_widget_hide (dialog->priv->update_link_label);
+                gtk_widget_show (dialog->priv->updating_spinner);
+                gtk_widget_show (dialog->priv->updating_label);
+                gdu_spinner_start (GDU_SPINNER (dialog->priv->updating_spinner));
+        } else {
+                gtk_widget_hide (dialog->priv->updating_spinner);
+                gdu_spinner_stop (GDU_SPINNER (dialog->priv->updating_spinner));
+                gtk_widget_hide (dialog->priv->updating_label);
+                gtk_widget_show (dialog->priv->updated_label);
+                if (dialog->priv->device == NULL) {
+                        gtk_widget_hide (dialog->priv->update_link_label);
                 } else {
-                        tip_updates_str = _("Only when performing a self-test.");
+                        gtk_widget_show (dialog->priv->update_link_label);
                 }
+        }
 
-                tooltip_str = g_strdup_printf (_("<b>Type:</b> %s\n"
-                                                 "<b>Updates:</b> %s\n"
-                                                 "<b>Description</b>: %s"),
-                                               tip_type_str,
-                                               tip_updates_str,
-                                               desc_str);
-
-                current_str = g_strdup_printf ("%d", gdu_ata_smart_attribute_get_current (a));
-                worst_str = g_strdup_printf ("%d", gdu_ata_smart_attribute_get_worst (a));
-                threshold_str = g_strdup_printf ("%d", gdu_ata_smart_attribute_get_threshold (a));
-
-                if (gdu_ata_smart_attribute_get_flags (a) & 0x0002)
-                        updates_str = _("Online");
-                else
-                        updates_str = _("Offline");
-
-                if (gdu_ata_smart_attribute_get_flags (a) & 0x0001)
-                        type_str = _("Pre-fail");
+        /* control visibility of self-test widgets */
+        if (dialog->priv->device != NULL && is_self_test_running (dialog->priv->device, &test_type)) {
+                gdouble fraction;
+                const gchar *test_type_str;
+
+                fraction = gdu_device_job_get_percentage (dialog->priv->device) / 100.0;
+                if (fraction < 0.0)
+                        fraction = 0.0;
+                if (fraction > 1.0)
+                        fraction = 1.0;
+
+                gtk_widget_show (dialog->priv->self_test_result_label);
+                gtk_widget_hide (dialog->priv->self_test_run_link_label);
+                gtk_widget_show (dialog->priv->self_test_progress_bar);
+                if (gdu_device_job_is_cancellable (dialog->priv->device))
+                        gtk_widget_show (dialog->priv->self_test_cancel_link_label);
                 else
-                        type_str = _("Old-age");
-
-                pretty_value = gdu_ata_smart_attribute_get_pretty_value (a);
-                pretty_unit = gdu_ata_smart_attribute_get_pretty_unit (a);
-                pretty_str = pretty_to_string (pretty_value, pretty_unit, TRUE);
-
-                is_good = gdu_ata_smart_attribute_get_good (a);
-                is_good_valid = gdu_ata_smart_attribute_get_good_valid (a);
-
-                if (!gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, &icon_height))
-                        icon_height = 48;
-
-                if (!is_good_valid) {
-                                status_pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
-                                                                          "gdu-smart-unknown",
-                                                                          icon_height,
-                                                                          GTK_ICON_LOOKUP_GENERIC_FALLBACK,
-                                                                          NULL);
-                                status_str = g_strdup (_("N/A"));
+                        gtk_widget_hide (dialog->priv->self_test_cancel_link_label);
+                gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->priv->self_test_progress_bar),
+                                               fraction);
+
+                switch (test_type) {
+                case SK_SMART_SELF_TEST_SHORT:
+                        /* Translators: Shown in the "Self-tests" item in the status table when a test is underway */
+                        test_type_str = _("Short self-test in progress: ");
+                        break;
+                case SK_SMART_SELF_TEST_EXTENDED:
+                        /* Translators: Shown in the "Self-tests" item in the status table when a test is underway */
+                        test_type_str = _("Extended self-test in progress: ");
+                        break;
+                case SK_SMART_SELF_TEST_CONVEYANCE:
+                        /* Translators: Shown in the "Self-tests" item in the status table when a test is underway */
+                        test_type_str = _("Conveyance self-test in progress: ");
+                        break;
+                default:
+                        g_assert_not_reached ();
+                        break;
+                }
+                gtk_label_set_markup (GTK_LABEL (dialog->priv->self_test_result_label),
+                                      test_type_str);
+        } else {
+                gtk_widget_hide (dialog->priv->self_test_progress_bar);
+                gtk_widget_hide (dialog->priv->self_test_cancel_link_label);
+                gtk_widget_show (dialog->priv->self_test_result_label);
+                if (dialog->priv->device == NULL) {
+                        gtk_widget_hide (dialog->priv->self_test_run_link_label);
                 } else {
-                        if (is_good) {
-                                status_pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
-                                                                          "gdu-smart-healthy",
-                                                                          icon_height,
-                                                                          GTK_ICON_LOOKUP_GENERIC_FALLBACK,
-                                                                          NULL);
-                                status_str = g_strdup (_("OK"));
-                        } else {
-                                status_pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
-                                                                          "gdu-smart-failing",
-                                                                          icon_height,
-                                                                          GTK_ICON_LOOKUP_GENERIC_FALLBACK,
-                                                                          NULL);
-                                status_str = g_strdup (_("<span foreground='red'><b>FAILING</b></span>"));
-                        }
+                        gtk_widget_show (dialog->priv->self_test_run_link_label);
                 }
-
-                gtk_list_store_append (dialog->priv->attr_list_store, &iter);
-                gtk_list_store_set (dialog->priv->attr_list_store, &iter,
-                                    ATTR_NAME_COLUMN, gdu_ata_smart_attribute_get_name (a),
-                                    ATTR_ID_INT_COLUMN, gdu_ata_smart_attribute_get_id (a),
-                                    ATTR_ID_COLUMN, col_str,
-                                    ATTR_DESC_COLUMN, name_str,
-                                    ATTR_CURRENT_COLUMN, current_str,
-                                    ATTR_WORST_COLUMN, worst_str,
-                                    ATTR_THRESHOLD_COLUMN, threshold_str,
-                                    ATTR_VALUE_COLUMN, pretty_str,
-                                    ATTR_STATUS_PIXBUF_COLUMN, status_pixbuf,
-                                    ATTR_STATUS_TEXT_COLUMN, status_str,
-                                    ATTR_TYPE_COLUMN, type_str,
-                                    ATTR_UPDATES_COLUMN, updates_str,
-                                    ATTR_TOOLTIP_COLUMN, tooltip_str,
-                                    -1);
-                g_free (col_str);
-                g_free (name_str);
-                g_free (current_str);
-                g_free (worst_str);
-                g_free (threshold_str);
-                g_free (pretty_str);
-                g_object_unref (status_pixbuf);
-                g_free (status_str);
-                g_free (tooltip_str);
-                g_free (desc_str);
-
         }
 
-        g_free (assessment_text);
+        if (sk_disk != NULL)
+                sk_disk_free (sk_disk);
+
+ out:
+        g_free (overall_assessment_text);
+        g_free (self_assessment_text);
         g_free (powered_on_text);
+        g_free (model_text);
+        g_free (firmware_text);
+        g_free (serial_text);
         g_free (temperature_text);
         g_free (selftest_text);
-
-        /* TODO: also fetch new data if current data is out of date */
-        if (dialog->priv->historical_data == NULL) {
-                gdu_device_drive_ata_smart_get_historical_data (dialog->priv->device,
-                                                                0, /* since */
-                                                                0, /* until */
-                                                                0, /* spacing */
-                                                                get_historical_data_cb,
-                                                                g_object_ref (dialog));
-        }
+        if (status_icon != NULL)
+                g_object_unref (status_icon);
 }
 
 static void
@@ -1229,3 +2800,12 @@ device_changed (GduDevice *device,
         }
 
 }
+
+static void
+device_job_changed (GduDevice *device,
+                    gpointer   user_data)
+{
+        GduAtaSmartDialog *dialog = GDU_ATA_SMART_DIALOG (user_data);
+
+        update_dialog (dialog);
+}
diff --git a/src/gdu-gtk/gdu-ata-smart-dialog.h b/src/gdu-gtk/gdu-ata-smart-dialog.h
index d441c9c..a2f9d10 100644
--- a/src/gdu-gtk/gdu-ata-smart-dialog.h
+++ b/src/gdu-gtk/gdu-ata-smart-dialog.h
@@ -55,7 +55,7 @@ struct GduAtaSmartDialogClass
 
 GType       gdu_ata_smart_dialog_get_type (void) G_GNUC_CONST;
 GtkWidget*  gdu_ata_smart_dialog_new      (GtkWindow *parent,
-                                           GduDevice *device);
+                                           GduDrive  *drive);
 
 G_END_DECLS
 
diff --git a/src/gdu-gtk/gdu-gtk-enums.h b/src/gdu-gtk/gdu-gtk-enums.h
index 844a632..938f36c 100644
--- a/src/gdu-gtk/gdu-gtk-enums.h
+++ b/src/gdu-gtk/gdu-gtk-enums.h
@@ -28,30 +28,6 @@
 
 #include <glib-object.h>
 
-typedef enum {
-        GDU_CURVE_FLAGS_NONE                = 0,
-        GDU_CURVE_FLAGS_FILLED              = (1 << 0),
-        GDU_CURVE_FLAGS_FADE_EDGES          = (1 << 1),
-        GDU_CURVE_FLAGS_AXIS_MARKERS_LEFT   = (1 << 2),
-        GDU_CURVE_FLAGS_AXIS_MARKERS_RIGHT  = (1 << 3),
-        GDU_CURVE_FLAGS_NORMALIZE           = (1 << 4),
-} GduCurveFlags;
-
-/**
- * GduCurveUnit:
- * @GDU_CURVE_UNIT_FLOATING: Value is a floating point number.
- * @GDU_CURVE_UNIT_INTEGER: Value is an integer number, such as a count of some sort.
- * @GDU_CURVE_UNIT_TIME_SECONDS: Value is a measure of time, in seconds.
- * @GDU_CURVE_UNIT_TEMPERATURE_KELVIN: Value is a measure of temperature, in degrees Kelvin.
- *
- */
-typedef enum {
-        GDU_CURVE_UNIT_FLOATING            = 0,
-        GDU_CURVE_UNIT_INTEGER             = 1,
-        GDU_CURVE_UNIT_TIME_SECONDS        = 2,
-        GDU_CURVE_UNIT_TEMPERATURE_KELVIN  = 3
-} GduCurveUnit;
-
 /**
  * GduPoolTreeModelColumn:
  * @GDU_POOL_TREE_MODEL_COLUMN_ICON: The #GIcon for the presentable.
diff --git a/src/gdu-gtk/gdu-gtk-enumtypes.h b/src/gdu-gtk/gdu-gtk-enumtypes.h
index 5df9d8b..35aa1c6 100644
--- a/src/gdu-gtk/gdu-gtk-enumtypes.h
+++ b/src/gdu-gtk/gdu-gtk-enumtypes.h
@@ -9,10 +9,6 @@
 G_BEGIN_DECLS
 
 /* enumerations from "gdu-gtk-enums.h" */
-GType gdu_curve_flags_get_type (void) G_GNUC_CONST;
-#define GDU_TYPE_CURVE_FLAGS (gdu_curve_flags_get_type ())
-GType gdu_curve_unit_get_type (void) G_GNUC_CONST;
-#define GDU_TYPE_CURVE_UNIT (gdu_curve_unit_get_type ())
 GType gdu_pool_tree_model_column_get_type (void) G_GNUC_CONST;
 #define GDU_TYPE_POOL_TREE_MODEL_COLUMN (gdu_pool_tree_model_column_get_type ())
 GType gdu_pool_tree_view_flags_get_type (void) G_GNUC_CONST;
diff --git a/src/gdu-gtk/gdu-gtk-types.h b/src/gdu-gtk/gdu-gtk-types.h
index 34465da..fca5641 100644
--- a/src/gdu-gtk/gdu-gtk-types.h
+++ b/src/gdu-gtk/gdu-gtk-types.h
@@ -39,6 +39,7 @@ typedef struct GduCurve                    GduCurve;
 typedef struct GduGraph                    GduGraph;
 typedef struct GduTimeLabel                GduTimeLabel;
 typedef struct GduAtaSmartDialog           GduAtaSmartDialog;
+typedef struct GduSpinner                  GduSpinner;
 
 struct GduPoolTreeModel;
 typedef struct GduPoolTreeModel         GduPoolTreeModel;
diff --git a/src/gdu-gtk/gdu-gtk.h b/src/gdu-gtk/gdu-gtk.h
index 750e496..dccb785 100644
--- a/src/gdu-gtk/gdu-gtk.h
+++ b/src/gdu-gtk/gdu-gtk.h
@@ -34,9 +34,8 @@
 #include <gdu-gtk/gdu-pool-tree-model.h>
 #include <gdu-gtk/gdu-size-widget.h>
 #include <gdu-gtk/gdu-create-linux-md-dialog.h>
-#include <gdu-gtk/gdu-curve.h>
-#include <gdu-gtk/gdu-graph.h>
 #include <gdu-gtk/gdu-ata-smart-dialog.h>
+#include <gdu-gtk/gdu-spinner.h>
 #undef __GDU_GTK_INSIDE_GDU_GTK_H
 
 G_BEGIN_DECLS
diff --git a/src/gdu-gtk/gdu-pool-tree-view.c b/src/gdu-gtk/gdu-pool-tree-view.c
index 1cab240..05ec317 100644
--- a/src/gdu-gtk/gdu-pool-tree-view.c
+++ b/src/gdu-gtk/gdu-pool-tree-view.c
@@ -226,7 +226,7 @@ pixbuf_data_func (GtkCellLayout   *cell_layout,
                   GtkTreeIter     *iter,
                   gpointer         user_data)
 {
-        GduPoolTreeView *view = GDU_POOL_TREE_VIEW (user_data);
+        //GduPoolTreeView *view = GDU_POOL_TREE_VIEW (user_data);
         GduPresentable *p;
         GIcon *icon;
 
diff --git a/src/gdu/Makefile.am b/src/gdu/Makefile.am
index ed890d3..b81700a 100644
--- a/src/gdu/Makefile.am
+++ b/src/gdu/Makefile.am
@@ -32,8 +32,6 @@ libgduinclude_HEADERS =              			\
 	gdu-pool.h					\
 	gdu-presentable.h				\
 	gdu-process.h					\
-	gdu-ata-smart-attribute.h			\
-	gdu-ata-smart-historical-data.h			\
 	gdu-util.h					\
 	gdu-volume.h					\
 	gdu-volume-hole.h
@@ -44,8 +42,6 @@ libgdu_la_SOURCES =                                					\
 						gdu-callbacks.h				\
 	gdu-util.h				gdu-util.c				\
 	gdu-pool.c				gdu-pool.h				\
-	gdu-ata-smart-historical-data.c		gdu-ata-smart-historical-data.h		\
-	gdu-ata-smart-attribute.c		gdu-ata-smart-attribute.h		\
 	gdu-device.c				gdu-device.h				\
 	gdu-drive.c				gdu-drive.h				\
 	gdu-linux-md-drive.c			gdu-linux-md-drive.h			\
diff --git a/src/gdu/gdu-callbacks.h b/src/gdu/gdu-callbacks.h
index 8f8057e..31bc81a 100644
--- a/src/gdu/gdu-callbacks.h
+++ b/src/gdu/gdu-callbacks.h
@@ -122,11 +122,6 @@ typedef void (*GduDeviceCancelJobCompletedFunc) (GduDevice  *device,
                                                  GError     *error,
                                                  gpointer    user_data);
 
-typedef void (*GduDeviceDriveAtaSmartGetHistoricalDataCompletedFunc) (GduDevice *device,
-                                                                      GList     *smart_data,
-                                                                      GError    *error,
-                                                                      gpointer   user_data);
-
 typedef void (*GduDeviceFilesystemListOpenFilesCompletedFunc) (GduDevice    *device,
                                                                GList        *processes,
                                                                GError       *error,
diff --git a/src/gdu/gdu-device.c b/src/gdu/gdu-device.c
index ab3736d..a38b08b 100644
--- a/src/gdu/gdu-device.c
+++ b/src/gdu/gdu-device.c
@@ -31,7 +31,6 @@
 #include "gdu-private.h"
 #include "gdu-pool.h"
 #include "gdu-device.h"
-#include "gdu-ata-smart-attribute.h"
 #include "devkit-disks-device-glue.h"
 
 /* --- SUCKY CODE BEGIN --- */
@@ -130,25 +129,10 @@ typedef struct
         guint optical_disc_num_sessions;
 
         gboolean drive_ata_smart_is_available;
-        gboolean drive_ata_smart_is_failing;
-        gboolean drive_ata_smart_is_failing_valid;
-        gboolean drive_ata_smart_has_bad_sectors;
-        gboolean drive_ata_smart_has_bad_attributes;
-        gdouble drive_ata_smart_temperature_kelvin;
-        guint64 drive_ata_smart_power_on_seconds;
         guint64 drive_ata_smart_time_collected;
-        guint drive_ata_smart_offline_data_collection_status;
-        guint drive_ata_smart_offline_data_collection_seconds;
-        guint drive_ata_smart_self_test_execution_status;
-        guint drive_ata_smart_self_test_execution_percent_remaining;
-        gboolean drive_ata_smart_short_and_extended_self_test_available;
-        gboolean drive_ata_smart_conveyance_self_test_available;
-        gboolean drive_ata_smart_start_self_test_available;
-        gboolean drive_ata_smart_abort_self_test_available;
-        guint drive_ata_smart_short_self_test_polling_minutes;
-        guint drive_ata_smart_extended_self_test_polling_minutes;
-        guint drive_ata_smart_conveyance_self_test_polling_minutes;
-        GValue drive_ata_smart_attributes;
+        gchar *drive_ata_smart_status;
+        gchar *drive_ata_smart_blob;
+        gsize drive_ata_smart_blob_size;
 
         char    *linux_md_component_level;
         int      linux_md_component_num_raid_devices;
@@ -340,44 +324,15 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props)
 
         else if (strcmp (key, "drive-ata-smart-is-available") == 0)
                 props->drive_ata_smart_is_available = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-ata-smart-is-failing") == 0)
-                props->drive_ata_smart_is_failing = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-ata-smart-is-failing-valid") == 0)
-                props->drive_ata_smart_is_failing_valid = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-ata-smart-has-bad-sectors") == 0)
-                props->drive_ata_smart_has_bad_sectors = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-ata-smart-has-bad-attributes") == 0)
-                props->drive_ata_smart_has_bad_attributes = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-ata-smart-temperature-kelvin") == 0)
-                props->drive_ata_smart_temperature_kelvin = g_value_get_double (value);
-        else if (strcmp (key, "drive-ata-smart-power-on-seconds") == 0)
-                props->drive_ata_smart_power_on_seconds = g_value_get_uint64 (value);
         else if (strcmp (key, "drive-ata-smart-time-collected") == 0)
                 props->drive_ata_smart_time_collected = g_value_get_uint64 (value);
-        else if (strcmp (key, "drive-ata-smart-offline-data-collection-status") == 0)
-                props->drive_ata_smart_offline_data_collection_status = g_value_get_uint (value);
-        else if (strcmp (key, "drive-ata-smart-offline-data-collection-seconds") == 0)
-                props->drive_ata_smart_offline_data_collection_seconds = g_value_get_uint (value);
-        else if (strcmp (key, "drive-ata-smart-self-test-execution-status") == 0)
-                props->drive_ata_smart_self_test_execution_status = g_value_get_uint (value);
-        else if (strcmp (key, "drive-ata-smart-self-test-execution-percent-remaining") == 0)
-                props->drive_ata_smart_self_test_execution_percent_remaining = g_value_get_uint (value);
-        else if (strcmp (key, "drive-ata-smart-short-and-extended-self-test-available") == 0)
-                props->drive_ata_smart_short_and_extended_self_test_available = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-ata-smart-conveyance-self-test-available") == 0)
-                props->drive_ata_smart_conveyance_self_test_available = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-ata-smart-start-self-test-available") == 0)
-                props->drive_ata_smart_start_self_test_available = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-ata-smart-abort-self-test-available") == 0)
-                props->drive_ata_smart_abort_self_test_available = g_value_get_boolean (value);
-        else if (strcmp (key, "drive-ata-smart-short-self-test-polling-minutes") == 0)
-                props->drive_ata_smart_short_self_test_polling_minutes = g_value_get_uint (value);
-        else if (strcmp (key, "drive-ata-smart-extended-self-test-polling-minutes") == 0)
-                props->drive_ata_smart_extended_self_test_polling_minutes = g_value_get_uint (value);
-        else if (strcmp (key, "drive-ata-smart-conveyance-self-test-polling-minutes") == 0)
-                props->drive_ata_smart_conveyance_self_test_polling_minutes = g_value_get_uint (value);
-        else if (strcmp (key, "drive-ata-smart-attributes") == 0) {
-                g_value_copy (value, &(props->drive_ata_smart_attributes));
+        else if (strcmp (key, "drive-ata-smart-status") == 0)
+                props->drive_ata_smart_status = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "drive-ata-smart-blob") == 0) {
+                GArray *a = g_value_get_boxed (value);
+                g_free (props->drive_ata_smart_blob);
+                props->drive_ata_smart_blob = g_memdup (a->data, a->len);
+                props->drive_ata_smart_blob_size = a->len;
         }
 
         else if (strcmp (key, "linux-md-component-level") == 0)
@@ -470,7 +425,8 @@ device_properties_free (DeviceProperties *props)
         g_strfreev (props->drive_media_compatibility);
         g_free (props->drive_media);
 
-        g_value_unset (&(props->drive_ata_smart_attributes));
+        g_free (props->drive_ata_smart_status);
+        g_free (props->drive_ata_smart_blob);
 
         g_free (props->linux_md_component_level);
         g_free (props->linux_md_component_uuid);
@@ -502,8 +458,6 @@ device_properties_get (DBusGConnection *bus,
         const char *ifname = "org.freedesktop.DeviceKit.Disks.Device";
 
         props = g_new0 (DeviceProperties, 1);
-        g_value_init (&(props->drive_ata_smart_attributes),
-                      dbus_g_type_get_collection ("GPtrArray", ATA_SMART_ATTRIBUTE_STRUCT_TYPE));
 
 	prop_proxy = dbus_g_proxy_new_for_name (bus,
                                                 "org.freedesktop.DeviceKit.Disks",
@@ -1297,149 +1251,26 @@ gdu_device_drive_ata_smart_get_is_available (GduDevice *device)
         return device->priv->props->drive_ata_smart_is_available;
 }
 
-gboolean
-gdu_device_drive_ata_smart_get_is_failing (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_is_failing;
-}
-
-gboolean
-gdu_device_drive_ata_smart_get_is_failing_valid (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_is_failing_valid;
-}
-
-gboolean
-gdu_device_drive_ata_smart_get_has_bad_sectors (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_has_bad_sectors;
-}
-
-gboolean
-gdu_device_drive_ata_smart_get_has_bad_attributes (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_has_bad_attributes;
-}
-
-gdouble
-gdu_device_drive_ata_smart_get_temperature_kelvin (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_temperature_kelvin;
-}
-
-guint64 gdu_device_drive_ata_smart_get_power_on_seconds (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_power_on_seconds;
-}
-
 guint64
 gdu_device_drive_ata_smart_get_time_collected (GduDevice *device)
 {
         return device->priv->props->drive_ata_smart_time_collected;
 }
 
-GduAtaSmartOfflineDataCollectionStatus
-gdu_device_drive_ata_smart_get_offline_data_collection_status (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_offline_data_collection_status;
-}
-
-guint
-gdu_device_drive_ata_smart_get_offline_data_collection_seconds (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_offline_data_collection_seconds;
-}
-
-GduAtaSmartSelfTestExecutionStatus
-gdu_device_drive_ata_smart_get_self_test_execution_status (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_self_test_execution_status;
-}
-
-guint
-gdu_device_drive_ata_smart_get_self_test_execution_percent_remaining (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_self_test_execution_percent_remaining;
-}
-
-gboolean
-gdu_device_drive_ata_smart_get_short_and_extended_self_test_available (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_short_and_extended_self_test_available;
-}
-
-gboolean
-gdu_device_drive_ata_smart_get_conveyance_self_test_available (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_conveyance_self_test_available;
-}
-
-gboolean
-gdu_device_drive_ata_smart_get_start_self_test_available (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_start_self_test_available;
-}
-
-gboolean
-gdu_device_drive_ata_smart_get_abort_self_test_available (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_abort_self_test_available;
-}
-
-guint
-gdu_device_drive_ata_smart_get_short_self_test_polling_minutes (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_short_self_test_polling_minutes;
-}
-
-guint
-gdu_device_drive_ata_smart_get_extended_self_test_polling_minutes (GduDevice *device)
-{
-        return device->priv->props->drive_ata_smart_extended_self_test_polling_minutes;
-}
-
-guint
-gdu_device_drive_ata_smart_get_conveyance_self_test_polling_minutes (GduDevice *device)
+const gchar *
+gdu_device_drive_ata_smart_get_status (GduDevice *device)
 {
-        return device->priv->props->drive_ata_smart_conveyance_self_test_polling_minutes;
+        return device->priv->props->drive_ata_smart_status;
 }
 
-GList *
-gdu_device_drive_ata_smart_get_attributes (GduDevice *device)
+gconstpointer
+gdu_device_drive_ata_smart_get_blob (GduDevice *device, gsize *out_size)
 {
-        GList *ret;
-        GPtrArray *p;
-        guint n;
+        gconstpointer ret;
 
-        ret = NULL;
-
-        p = g_value_get_boxed (&(device->priv->props->drive_ata_smart_attributes));
-        for (n = 0; n < p->len; n++) {
-                ret = g_list_prepend (ret, _gdu_ata_smart_attribute_new (p->pdata[n]));
-        }
-
-        return ret;
-}
-
-GduAtaSmartAttribute *
-gdu_device_drive_ata_smart_get_attribute (GduDevice *device, const gchar *attr_name)
-{
-        GList *attrs;
-        GList *l;
-        GduAtaSmartAttribute *ret;
-
-        ret = NULL;
-
-        attrs = gdu_device_drive_ata_smart_get_attributes (device);
-        for (l = attrs; l != NULL; l = l->next) {
-                GduAtaSmartAttribute *a = GDU_ATA_SMART_ATTRIBUTE (l->data);
-                if (g_strcmp0 (attr_name, gdu_ata_smart_attribute_get_name (a)) == 0) {
-                        ret = g_object_ref (a);
-                        break;
-                }
-        }
-        g_list_foreach (attrs, (GFunc) g_object_unref, NULL);
-        g_list_free (attrs);
+        ret = device->priv->props->drive_ata_smart_blob;
+        if (out_size != NULL)
+                *out_size = device->priv->props->drive_ata_smart_blob_size;
 
         return ret;
 }
@@ -2394,94 +2225,6 @@ gdu_device_op_cancel_job (GduDevice *device, GduDeviceCancelJobCompletedFunc cal
 
 typedef struct {
         GduDevice *device;
-        GduDeviceDriveAtaSmartGetHistoricalDataCompletedFunc callback;
-        gpointer user_data;
-} DriveAtaSmartGetHistoricalDataData;
-
-static GList *
-op_ata_smart_historical_data_compute_ret (GPtrArray *historical_data)
-{
-        GList *ret;
-        int n;
-
-        ret = NULL;
-        for (n = 0; n < (int) historical_data->len; n++) {
-                ret = g_list_prepend (ret, _gdu_ata_smart_historical_data_new (historical_data->pdata[n]));
-        }
-        ret = g_list_reverse (ret);
-        return ret;
-}
-
-static void
-op_ata_smart_historical_data_cb (DBusGProxy *proxy, GPtrArray *historical_data, GError *error, gpointer user_data)
-{
-        DriveAtaSmartGetHistoricalDataData *data = user_data;
-        GList *ret;
-
-        _gdu_error_fixup (error);
-
-        ret = NULL;
-        if (historical_data != NULL && error == NULL)
-                ret = op_ata_smart_historical_data_compute_ret (historical_data);
-
-        if (data->callback != NULL)
-                data->callback (data->device, ret, error, data->user_data);
-
-        g_object_unref (data->device);
-        g_free (data);
-}
-
-void
-gdu_device_drive_ata_smart_get_historical_data (GduDevice                                         *device,
-                                                guint64                                            since,
-                                                guint64                                            until,
-                                                guint64                                            spacing,
-                                                GduDeviceDriveAtaSmartGetHistoricalDataCompletedFunc  callback,
-                                                gpointer                                           user_data)
-{
-        DriveAtaSmartGetHistoricalDataData *data;
-
-        data = g_new0 (DriveAtaSmartGetHistoricalDataData, 1);
-        data->device = g_object_ref (device);
-        data->callback = callback;
-        data->user_data = user_data;
-
-        org_freedesktop_DeviceKit_Disks_Device_drive_ata_smart_get_historical_data_async (device->priv->proxy,
-                                                                                          since,
-                                                                                          until,
-                                                                                          spacing,
-                                                                                          op_ata_smart_historical_data_cb,
-                                                                                          data);
-}
-
-GList *
-gdu_device_drive_ata_smart_get_historical_data_sync (GduDevice  *device,
-                                                     guint64     since,
-                                                     guint64     until,
-                                                     guint64     spacing,
-                                                     GError    **error)
-{
-        GList *ret;
-        GPtrArray *historical_data;
-
-        ret = NULL;
-        if (!org_freedesktop_DeviceKit_Disks_Device_drive_ata_smart_get_historical_data (device->priv->proxy,
-                                                                                         since,
-                                                                                         until,
-                                                                                         spacing,
-                                                                                         &historical_data,
-                                                                                         error))
-                goto out;
-
-        ret = op_ata_smart_historical_data_compute_ret (historical_data);
-out:
-        return ret;
-}
-
-/* -------------------------------------------------------------------------------- */
-
-typedef struct {
-        GduDevice *device;
         GduDeviceDriveEjectCompletedFunc callback;
         gpointer user_data;
 } DriveEjectData;
diff --git a/src/gdu/gdu-device.h b/src/gdu/gdu-device.h
index 0ef276b..8b9fddf 100644
--- a/src/gdu/gdu-device.h
+++ b/src/gdu/gdu-device.h
@@ -171,50 +171,11 @@ const char *gdu_device_linux_md_get_sync_action (GduDevice *device);
 double      gdu_device_linux_md_get_sync_percentage (GduDevice *device);
 guint64     gdu_device_linux_md_get_sync_speed (GduDevice *device);
 
-typedef enum {
-        GDU_ATA_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER,
-        GDU_ATA_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS,
-        GDU_ATA_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS,
-        GDU_ATA_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED,
-        GDU_ATA_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED,
-        GDU_ATA_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL,
-        GDU_ATA_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN,
-} GduAtaSmartOfflineDataCollectionStatus;
-
-typedef enum {
-        GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER = 0,
-        GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED = 1,
-        GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED = 2,
-        GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_FATAL = 3,
-        GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN = 4,
-        GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL = 5,
-        GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO = 6,
-        GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ = 7,
-        GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING = 8,
-        GDU_ATA_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS = 15,
-} GduAtaSmartSelfTestExecutionStatus;
-
-gboolean    gdu_device_drive_ata_smart_get_is_available (GduDevice *device);
-gboolean    gdu_device_drive_ata_smart_get_is_failing (GduDevice *device);
-gboolean    gdu_device_drive_ata_smart_get_is_failing_valid (GduDevice *device);
-gboolean    gdu_device_drive_ata_smart_get_has_bad_sectors (GduDevice *device);
-gboolean    gdu_device_drive_ata_smart_get_has_bad_attributes (GduDevice *device);
-gdouble     gdu_device_drive_ata_smart_get_temperature_kelvin (GduDevice *device);
-guint64     gdu_device_drive_ata_smart_get_power_on_seconds (GduDevice *device);
-guint64     gdu_device_drive_ata_smart_get_time_collected (GduDevice *device);
-GduAtaSmartOfflineDataCollectionStatus gdu_device_drive_ata_smart_get_offline_data_collection_status (GduDevice *device);
-guint       gdu_device_drive_ata_smart_get_offline_data_collection_seconds (GduDevice *device);
-GduAtaSmartSelfTestExecutionStatus gdu_device_drive_ata_smart_get_self_test_execution_status (GduDevice *device);
-guint       gdu_device_drive_ata_smart_get_self_test_execution_percent_remaining (GduDevice *device);
-gboolean    gdu_device_drive_ata_smart_get_short_and_extended_self_test_available (GduDevice *device);
-gboolean    gdu_device_drive_ata_smart_get_conveyance_self_test_available (GduDevice *device);
-gboolean    gdu_device_drive_ata_smart_get_start_self_test_available (GduDevice *device);
-gboolean    gdu_device_drive_ata_smart_get_abort_self_test_available (GduDevice *device);
-guint       gdu_device_drive_ata_smart_get_short_self_test_polling_minutes (GduDevice *device);
-guint       gdu_device_drive_ata_smart_get_extended_self_test_polling_minutes (GduDevice *device);
-guint       gdu_device_drive_ata_smart_get_conveyance_self_test_polling_minutes (GduDevice *device);
-GList      *gdu_device_drive_ata_smart_get_attributes (GduDevice *device);
-GduAtaSmartAttribute *gdu_device_drive_ata_smart_get_attribute (GduDevice *device, const gchar *attr_name);
+
+gboolean      gdu_device_drive_ata_smart_get_is_available (GduDevice *device);
+guint64       gdu_device_drive_ata_smart_get_time_collected (GduDevice *device);
+const gchar  *gdu_device_drive_ata_smart_get_status (GduDevice *device);
+gconstpointer gdu_device_drive_ata_smart_get_blob (GduDevice *device, gsize *out_size);
 
 /* ---------------------------------------------------------------------------------------------------- */
 
@@ -358,21 +319,6 @@ void gdu_device_op_cancel_job (GduDevice *device,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-void gdu_device_drive_ata_smart_get_historical_data (GduDevice                                         *device,
-                                                     guint64                                            since,
-                                                     guint64                                            until,
-                                                     guint64                                            spacing,
-                                                     GduDeviceDriveAtaSmartGetHistoricalDataCompletedFunc  callback,
-                                                     gpointer                                           user_data);
-
-GList *gdu_device_drive_ata_smart_get_historical_data_sync (GduDevice  *device,
-                                                            guint64     since,
-                                                            guint64     until,
-                                                            guint64     spacing,
-                                                            GError    **error);
-
-/* ---------------------------------------------------------------------------------------------------- */
-
 void gdu_device_filesystem_list_open_files (GduDevice                                     *device,
                                             GduDeviceFilesystemListOpenFilesCompletedFunc  callback,
                                             gpointer                                       user_data);
diff --git a/src/gdu/gdu-private.h b/src/gdu/gdu-private.h
index fe94862..6cb82d5 100644
--- a/src/gdu/gdu-private.h
+++ b/src/gdu/gdu-private.h
@@ -75,10 +75,6 @@
                                                      G_TYPE_STRING,   \
                                                      G_TYPE_INVALID))
 
-GduAtaSmartAttribute *_gdu_ata_smart_attribute_new   (gpointer data);
-
-GduAtaSmartHistoricalData * _gdu_ata_smart_historical_data_new            (gpointer data);
-
 GduKnownFilesystem    *_gdu_known_filesystem_new       (gpointer data);
 
 GduProcess            * _gdu_process_new               (gpointer data);
diff --git a/src/gdu/gdu-types.h b/src/gdu/gdu-types.h
index 8e3c826..de1a929 100644
--- a/src/gdu/gdu-types.h
+++ b/src/gdu/gdu-types.h
@@ -43,8 +43,6 @@ typedef struct _GduVolumeHole             GduVolumeHole;
 
 typedef struct _GduKnownFilesystem        GduKnownFilesystem;
 typedef struct _GduProcess                GduProcess;
-typedef struct _GduAtaSmartHistoricalData GduAtaSmartHistoricalData;
-typedef struct _GduAtaSmartAttribute      GduAtaSmartAttribute;
 
 G_END_DECLS
 
diff --git a/src/gdu/gdu-util.c b/src/gdu/gdu-util.c
index 27ccffc..7bf22d5 100644
--- a/src/gdu/gdu-util.c
+++ b/src/gdu/gdu-util.c
@@ -357,8 +357,12 @@ gdu_get_job_description (const char *job_id)
                 s = g_strdup (_("Checking RAID Array"));
         } else if (strcmp (job_id, "LinuxMdRepair") == 0) {
                 s = g_strdup (_("Repairing RAID Array"));
-        } else if (strcmp (job_id, "DriveAtaSmartInitiateSelftest") == 0) {
-                s = g_strdup (_("Running S.M.A.R.T. Self Test"));
+        } else if (strcmp (job_id, "DriveAtaSmartSelftestShort") == 0) {
+                s = g_strdup (_("Running Short SMART Self-Test"));
+        } else if (strcmp (job_id, "DriveAtaSmartSelftestExtended") == 0) {
+                s = g_strdup (_("Running Extended SMART Self-Test"));
+        } else if (strcmp (job_id, "DriveAtaSmartSelftestConveyance") == 0) {
+                s = g_strdup (_("Running Conveyance SMART Self-Test"));
         } else if (strcmp (job_id, "DriveEject") == 0) {
                 s = g_strdup (_("Ejecting Media"));
         } else if (strcmp (job_id, "DriveDetach") == 0) {
@@ -915,3 +919,86 @@ gdu_linux_md_get_raid_level_description (const gchar *linux_md_raid_level)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+/**
+ * gdu_util_ata_smart_status_to_desc:
+ * @status: Status from libatasmart, e.g. <quote>BAD_STATUS</quote>.
+ * @out_highlight: Return value (or %NULL) for suggesting whether the status should be highlighted.
+ * @out_action_text: Return value (or %NULL) for suggested action. Free with g_free().
+ * @out_icon: Return value (or %NULL) for icon to show. Free with g_object_unref().
+ *
+ * Gets a human readable representation of @status.
+ *
+ * Returns: The human readable status. Free with g_free().
+ */
+gchar *
+gdu_util_ata_smart_status_to_desc (const gchar  *status,
+                                   gboolean     *out_highlight,
+                                   gchar       **out_action_text,
+                                   GIcon       **out_icon)
+{
+        const gchar *desc;
+        const gchar *action_text;
+        gchar *ret;
+        gboolean highlight;
+        GIcon *icon;
+
+        highlight = FALSE;
+        action_text = NULL;
+        if (g_strcmp0 (status, "GOOD") == 0) {
+                /* Translators: Overall description of the GOOD status */
+                desc = _("Disk is healthy");
+                icon = g_themed_icon_new ("gdu-smart-healthy");
+        } else if (g_strcmp0 (status, "BAD_ATTRIBUTE_IN_THE_PAST") == 0) {
+                /* Translators: Overall description of the BAD_ATTRIBUTE_IN_THE_PAST status */
+                desc = _("Disk was used outside of design parameters in the past");
+                icon = g_themed_icon_new ("gdu-smart-healthy");
+        } else if (g_strcmp0 (status, "BAD_SECTOR") == 0) {
+                /* Translators: Overall description of the BAD_SECTOR status */
+                desc = _("Disk has a few bad sectors");
+                icon = g_themed_icon_new ("gdu-smart-healthy");
+        } else if (g_strcmp0 (status, "BAD_ATTRIBUTE_NOW") == 0) {
+                /* Translators: Overall description of the BAD_ATTRIBUTE_NOW status */
+                desc = _("DISK IS BEING USED OUTSIDE DESIGN PARAMETERS");
+                /* Translators: Suggested action for the BAD_ATTRIBUTE_NOW status */
+                action_text = _("Backup all data and replace the disk");
+                highlight = TRUE;
+                icon = g_themed_icon_new ("gdu-smart-threshold");
+        } else if (g_strcmp0 (status, "BAD_SECTOR_MANY") == 0) {
+                /* Translators: Overall description of the BAD_SECTOR_MANY status */
+                desc = _("DISK HAS MANY BAD SECTORS");
+                highlight = TRUE;
+                /* Translators: Suggested action for the BAD_SECTOR_MANY status */
+                action_text = _("Backup all data and replace the disk");
+                icon = g_themed_icon_new ("gdu-smart-threshold");
+        } else if (g_strcmp0 (status, "BAD_STATUS") == 0) {
+                /* Translators: Overall description of the BAD_STATUS status */
+                desc = _("DISK FAILURE IS IMMINENT");
+                /* Translators: Suggested action for the BAD_STATUS status */
+                action_text = _("Backup all data and replace the disk");
+                highlight = TRUE;
+                icon = g_themed_icon_new ("gdu-smart-failing");
+        } else if (status == NULL || strlen (status) == 0) {
+                /* Translators: Description when the overall SMART status is unknown */
+                desc = _("Unknown");
+                icon = g_themed_icon_new ("gdu-smart-unknown");
+        } else {
+                desc = status;
+                icon = g_themed_icon_new ("gdu-smart-unknown");
+        }
+
+        if (out_highlight != NULL)
+                *out_highlight = highlight;
+
+        if (out_action_text != NULL)
+                *out_action_text = g_strdup (action_text);
+
+        if (out_icon != NULL)
+                *out_icon = g_object_ref (icon);
+        g_object_unref (icon);
+
+        ret = g_strdup (desc);
+
+        return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/gdu/gdu-util.h b/src/gdu/gdu-util.h
index 2c1adb1..4305a98 100644
--- a/src/gdu/gdu-util.h
+++ b/src/gdu/gdu-util.h
@@ -50,6 +50,11 @@ char *gdu_linux_md_get_raid_level_for_display (const gchar *linux_md_raid_level,
 
 char *gdu_linux_md_get_raid_level_description (const gchar *linux_md_raid_level);
 
+gchar *gdu_util_ata_smart_status_to_desc (const gchar  *status,
+                                          gboolean     *out_highlight,
+                                          gchar       **out_action_text,
+                                          GIcon       **out_icon);
+
 typedef void (*GduUtilPartTypeForeachFunc) (const char *scheme,
                                             const char *type,
                                             const char *name,
diff --git a/src/gdu/gdu.h b/src/gdu/gdu.h
index 67aa4a2..5588b97 100644
--- a/src/gdu/gdu.h
+++ b/src/gdu/gdu.h
@@ -37,8 +37,6 @@
 #include <gdu/gdu-pool.h>
 #include <gdu/gdu-presentable.h>
 #include <gdu/gdu-process.h>
-#include <gdu/gdu-ata-smart-historical-data.h>
-#include <gdu/gdu-ata-smart-attribute.h>
 #include <gdu/gdu-util.h>
 #include <gdu/gdu-volume.h>
 #include <gdu/gdu-volume-hole.h>
diff --git a/src/notification/notification-main.c b/src/notification/notification-main.c
index 609f9ff..5f13e38 100644
--- a/src/notification/notification-main.c
+++ b/src/notification/notification-main.c
@@ -24,6 +24,8 @@
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 #include <stdlib.h>
+#include <glib/gstdio.h>
+#include <errno.h>
 
 #include <gdu/gdu.h>
 #include <gdu-gtk/gdu-gtk.h>
@@ -49,6 +51,8 @@ typedef struct
 
         NotifyNotification *ata_smart_notification;
 
+        GFileMonitor *ata_smart_ignore_monitor;
+
 } NotificationData;
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -132,10 +136,25 @@ on_status_icon_popup_menu (GtkStatusIcon *status_icon,
         show_menu_for_status_icon (data);
 }
 
+static void
+on_ata_smart_ignore_monitor_changed (GFileMonitor     *monitor,
+                                     GFile            *file,
+                                     GFile            *other_file,
+                                     GFileMonitorEvent event_type,
+                                     gpointer          user_data)
+{
+        NotificationData *data = user_data;
+        update_all (data);
+}
+
 static NotificationData *
 notification_data_new (void)
 {
         NotificationData *data;
+        GFile *file;
+        gchar *dir_path;
+        GError *error;
+        struct stat stat_buf;
 
         data = g_new0 (NotificationData, 1);
 
@@ -152,6 +171,37 @@ notification_data_new (void)
         g_signal_connect (data->status_icon, "activate", G_CALLBACK (on_status_icon_activate), data);
         g_signal_connect (data->status_icon, "popup-menu", G_CALLBACK (on_status_icon_popup_menu), data);
 
+        dir_path = g_build_filename (g_get_user_config_dir (),
+                                     "gnome-disk-utility",
+                                     "ata-smart-ignore",
+                                     NULL);
+        if (g_stat (dir_path, &stat_buf) != 0) {
+                if (g_mkdir_with_parents (dir_path, 0755) != 0) {
+                        g_warning ("Error creating directory `%s': %s",
+                                   dir_path,
+                                   g_strerror (errno));
+                }
+        }
+
+        file = g_file_new_for_path (dir_path);
+
+        error = NULL;
+        data->ata_smart_ignore_monitor = g_file_monitor_directory (file,
+                                                                   G_FILE_MONITOR_NONE,
+                                                                   NULL,
+                                                                   &error);
+        if (data->ata_smart_ignore_monitor != NULL) {
+                g_signal_connect (data->ata_smart_ignore_monitor,
+                                  "changed",
+                                  G_CALLBACK (on_ata_smart_ignore_monitor_changed),
+                                  data);
+        } else {
+                g_warning ("Error monitoring directory `%s': %s", dir_path, error->message);
+                g_error_free (error);
+        }
+        g_free (dir_path);
+        g_object_unref (file);
+
         return data;
 }
 
@@ -172,6 +222,11 @@ notification_data_free (NotificationData *data)
         g_list_free (data->devices_being_unmounted);
         g_list_foreach (data->ata_smart_failures, (GFunc) g_object_unref, NULL);
         g_list_free (data->ata_smart_failures);
+
+        if (data->ata_smart_ignore_monitor != NULL) {
+                g_object_unref (data->ata_smart_ignore_monitor);
+        }
+
         g_free (data);
 }
 
@@ -323,6 +378,39 @@ update_unmount_dialogs (NotificationData *data)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+/* TODO: keep in sync with src/gdu-gtk/gdu-ata-smart-dialog.c */
+static gboolean
+get_ata_smart_no_warn (GduDevice *device)
+{
+        gboolean ret;
+        gchar *path;
+        gchar *disk_id;
+        struct stat stat_buf;
+
+        ret = FALSE;
+
+        disk_id = g_strdup_printf ("%s-%s-%s-%s",
+                                   gdu_device_drive_get_vendor (device),
+                                   gdu_device_drive_get_model (device),
+                                   gdu_device_drive_get_revision (device),
+                                   gdu_device_drive_get_serial (device));
+
+        path = g_build_filename (g_get_user_config_dir (),
+                                 "gnome-disk-utility",
+                                 "ata-smart-ignore",
+                                 disk_id,
+                                 NULL);
+
+        if (g_stat (path, &stat_buf) == 0) {
+                ret = TRUE;
+        }
+
+        g_free (path);
+        g_free (disk_id);
+
+        return ret;
+}
+
 static void
 update_ata_smart_failures (NotificationData *data)
 {
@@ -338,14 +426,24 @@ update_ata_smart_failures (NotificationData *data)
 
         for (l = devices; l != NULL; l = l->next) {
                 GduDevice *device = GDU_DEVICE (l->data);
+                gboolean available;
+                const gchar *status;
+                guint64 time_collected;
+
+                available = gdu_device_drive_ata_smart_get_is_available (device);
+                time_collected = gdu_device_drive_ata_smart_get_time_collected (device);
+                status = gdu_device_drive_ata_smart_get_status (device);
 
-                if (!gdu_device_drive_ata_smart_get_is_available (device))
+                if (!available || time_collected == 0)
                         continue;
 
-                if (!((gdu_device_drive_ata_smart_get_is_failing (device) &&
-                       gdu_device_drive_ata_smart_get_is_failing_valid (device)) ||
-                      gdu_device_drive_ata_smart_get_has_bad_sectors (device) ||
-                      gdu_device_drive_ata_smart_get_has_bad_attributes (device)))
+                /* only warn on these statuses.. */
+                if (!((g_strcmp0 (status, "BAD_SECTOR_MANY") == 0) ||
+                      (g_strcmp0 (status, "BAD_ATTRIBUTE_NOW") == 0) ||
+                      (g_strcmp0 (status, "BAD_STATUS") == 0)))
+                        continue;
+
+                if (get_ata_smart_no_warn (device))
                         continue;
 
                 current = g_list_prepend (current, device);
@@ -432,11 +530,13 @@ update_status_icon (NotificationData *data)
         /* we've started showing the icon for ATA RAID failures; pop up a libnotify notification */
         if (old_show_icon_for_ata_smart_failures != data->show_icon_for_ata_smart_failures) {
 
-		data->ata_smart_notification = notify_notification_new
-                        (_("A hard disk is failing"),
-                         _("One or more hard disks report health problems. Click the icon to get more information."),
-                         "gtk-dialog-warning",
-                         NULL);
+		data->ata_smart_notification = notify_notification_new (
+                    /* Translators: This is used as the title of the notification */
+                    _("A hard disk may be failing"),
+                    /* Translators: This is used as the text of the notification*/
+                    _("One or more hard disks report health problems. Click the icon to get more information."),
+                    "gtk-dialog-warning",
+                    NULL);
                 notify_notification_attach_to_status_icon (data->ata_smart_notification,
                                                            data->status_icon);
                 notify_notification_set_urgency (data->ata_smart_notification, NOTIFY_URGENCY_CRITICAL);
@@ -502,18 +602,42 @@ show_menu_for_status_icon (NotificationData *data)
         for (l = data->ata_smart_failures; l != NULL; l = l->next) {
                 GduDevice *device = GDU_DEVICE (l->data);
                 GduPresentable *presentable;
-                gchar *device_name;
+                gchar *name;
+                gchar *vpd_name;
+                const gchar *status;
+                gchar *status_desc;
+                gboolean highlight;
                 GdkPixbuf *pixbuf;
                 GtkWidget *image;
                 GtkWidget *menu_item;
+                gchar *s;
 
                 presentable = gdu_pool_get_drive_by_device (data->pool, device);
-                device_name = gdu_presentable_get_name (presentable);
+                name = gdu_presentable_get_name (presentable);
+                vpd_name = gdu_presentable_get_vpd_name (presentable);
+
+                status = gdu_device_drive_ata_smart_get_status (device);
+                status_desc = gdu_util_ata_smart_status_to_desc (status, &highlight, NULL, NULL);
+
+                if (highlight) {
+                        s = g_strdup_printf ("<span fgcolor=\"red\"><b>%s</b></span>", status_desc);
+                        g_free (status_desc);
+                        status_desc = s;
+                }
+
+                s = g_strdup_printf ("<b>%s</b> â?? %s\n"
+                                     "<small>%s</small>",
+                                     name,
+                                     vpd_name,
+                                     status_desc);
+
+                menu_item = gtk_image_menu_item_new_with_label (s);
+                gtk_label_set_use_markup (GTK_LABEL (gtk_bin_get_child (GTK_BIN (menu_item))), TRUE);
+                gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menu_item), TRUE);
 
-                menu_item = gtk_image_menu_item_new_with_label (device_name);
                 g_object_set_data_full (G_OBJECT (menu_item), "gdu-device", g_object_ref (device), g_object_unref);
 
-                pixbuf = gdu_util_get_pixbuf_for_presentable (presentable, GTK_ICON_SIZE_MENU);
+                pixbuf = gdu_util_get_pixbuf_for_presentable (presentable, GTK_ICON_SIZE_SMALL_TOOLBAR);
                 image = gtk_image_new_from_pixbuf (pixbuf);
                 g_object_unref (pixbuf);
                 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), image);
@@ -525,7 +649,10 @@ show_menu_for_status_icon (NotificationData *data)
 
                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
 
-                g_free (device_name);
+                g_free (name);
+                g_free (vpd_name);
+                g_free (status_desc);
+                g_free (s);
                 g_object_unref (presentable);
         }
         gtk_widget_show_all (menu);
diff --git a/src/palimpsest/Makefile.am b/src/palimpsest/Makefile.am
index a59f258..42c8bdc 100644
--- a/src/palimpsest/Makefile.am
+++ b/src/palimpsest/Makefile.am
@@ -9,7 +9,6 @@ palimpsest_SOURCES = 									\
 						gdu-main.c				\
 	gdu-shell.h				gdu-shell.c				\
 	gdu-section.h				gdu-section.c				\
-	gdu-section-health.h			gdu-section-health.c			\
 	gdu-section-create-partition-table.h	gdu-section-create-partition-table.c	\
 	gdu-section-partition.h			gdu-section-partition.c			\
 	gdu-section-unallocated.h		gdu-section-unallocated.c		\
diff --git a/src/palimpsest/gdu-shell.c b/src/palimpsest/gdu-shell.c
index a397f70..fcb628f 100644
--- a/src/palimpsest/gdu-shell.c
+++ b/src/palimpsest/gdu-shell.c
@@ -34,7 +34,6 @@
 
 #include "gdu-shell.h"
 
-#include "gdu-section-health.h"
 #include "gdu-section-partition.h"
 #include "gdu-section-create-partition-table.h"
 #include "gdu-section-unallocated.h"
@@ -273,13 +272,43 @@ details_update (GduShell *shell)
                         g_ptr_array_add (details,
                                          g_strdup (_("Linux Software RAID")));
                 } else {
-                        s = gdu_util_get_connection_for_display (
-                                gdu_device_drive_get_connection_interface (device),
-                                gdu_device_drive_get_connection_speed (device));
-                        g_ptr_array_add (details,
-                                         /* Translators: %s is the name of a connection, like 'USB at 2 MB/s' */
-                                         g_strdup_printf (_("Connected via %s"), s));
-                        g_free (s);
+                        if (gdu_device_drive_ata_smart_get_is_available (device) &&
+                            gdu_device_drive_ata_smart_get_time_collected (device) > 0) {
+                                gchar *smart_status;
+                                gchar *status_desc;
+                                gboolean highlight;
+                                gboolean rtl;
+
+                                rtl = (gtk_widget_get_direction (GTK_WIDGET (shell->priv->app_window)) == GTK_TEXT_DIR_RTL);
+
+                                status_desc = gdu_util_ata_smart_status_to_desc (gdu_device_drive_ata_smart_get_status (device),
+                                                                                 &highlight,
+                                                                                 NULL,
+                                                                                 NULL);
+                                /* Translators: the %s is the SMART status of the disk e.g. 'Healthy' */
+                                if (highlight) {
+                                        s = g_strdup_printf ("<span fgcolor=\"red\"><b>%s</b></span>", status_desc);
+                                        g_free (status_desc);
+                                        status_desc = s;
+                                }
+                                smart_status = g_strdup_printf (_("SMART status: %s"),
+                                                                status_desc);
+                                g_free (status_desc);
+
+                                s = g_strdup_printf (rtl ? "<a href=\"gnome-disk-utility://show-smart\" title=\"%2$s\">%3$s</a> â?? %1$s" :
+                                                     "%s â?? <a href=\"gnome-disk-utility://show-smart\" title=\"%s\">%s</a>",
+                                                     smart_status,
+                                                     /* Translators: this the SMART hyperlink tooltip */
+                                                     _("View details about SMART for this disk"),
+                                                     /* Translators: this is the text for the SMART hyperlink */
+                                                     _("More Information"));
+                                g_free (smart_status);
+
+                                g_ptr_array_add (details, s);
+
+                        } else {
+                                g_ptr_array_add (details, g_strdup (_("SMART is not available")));
+                        }
                 }
 
                 if (device_file != NULL) {
@@ -425,6 +454,7 @@ details_update (GduShell *shell)
 
                 s = g_strdup_printf ("<span foreground='%s'>%s</span>", detail_color, detail_str);
                 gtk_label_set_markup (GTK_LABEL (label), s);
+                gtk_label_set_track_visited_links (GTK_LABEL (label), FALSE);
                 g_free (s);
         }
 
@@ -484,13 +514,6 @@ compute_sections_to_show (GduShell *shell)
 
                         sections_to_show = g_list_append (sections_to_show, (gpointer) GDU_TYPE_SECTION_NO_MEDIA);
 
-                } else {
-
-                        if (gdu_device_drive_ata_smart_get_is_available (device)) {
-                                sections_to_show = g_list_append (sections_to_show,
-                                                                  (gpointer) GDU_TYPE_SECTION_HEALTH);
-                        }
-
                 }
 
         } else if (GDU_IS_VOLUME (shell->priv->presentable_now_showing) && device != NULL) {
@@ -2120,6 +2143,33 @@ gdu_shell_raise_error (GduShell       *shell,
 }
 
 static void
+on_activate_link_for_details_label (GtkLabel    *label,
+                                    const gchar *uri,
+                                    gpointer     user_data)
+{
+        GduShell *shell = GDU_SHELL (user_data);
+
+        if (g_str_has_prefix (uri, "gnome-disk-utility://")) {
+
+                if (g_strcmp0 (uri, "gnome-disk-utility://show-smart") == 0) {
+                        if (GDU_IS_DRIVE (shell->priv->presentable_now_showing)) {
+                                GtkWidget *dialog;
+
+                                dialog = gdu_ata_smart_dialog_new (GTK_WINDOW (shell->priv->app_window),
+                                                                   GDU_DRIVE (shell->priv->presentable_now_showing));
+                                gtk_widget_show_all (dialog);
+                                gtk_dialog_run (GTK_DIALOG (dialog));
+                                gtk_widget_destroy (dialog);
+                        } else {
+                                g_warning ("Trying to show ATA SMART dialog for presentable that is not a drive");
+                        }
+                }
+
+                g_signal_stop_emission_by_name (label, "activate-link");
+        }
+}
+
+static void
 create_window (GduShell *shell)
 {
         GtkWidget *vbox;
@@ -2236,21 +2286,37 @@ create_window (GduShell *shell)
         label = gtk_label_new (NULL);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         gtk_box_pack_start (GTK_BOX (vbox3), label, FALSE, TRUE, 0);
+        g_signal_connect (label,
+                          "activate-link",
+                          G_CALLBACK (on_activate_link_for_details_label),
+                          shell);
         shell->priv->details0_label = label;
 
         label = gtk_label_new (NULL);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         gtk_box_pack_start (GTK_BOX (vbox3), label, FALSE, TRUE, 0);
+        g_signal_connect (label,
+                          "activate-link",
+                          G_CALLBACK (on_activate_link_for_details_label),
+                          shell);
         shell->priv->details1_label = label;
 
         label = gtk_label_new (NULL);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         gtk_box_pack_start (GTK_BOX (vbox3), label, FALSE, TRUE, 0);
+        g_signal_connect (label,
+                          "activate-link",
+                          G_CALLBACK (on_activate_link_for_details_label),
+                          shell);
         shell->priv->details2_label = label;
 
         label = gtk_label_new (NULL);
         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
         gtk_box_pack_start (GTK_BOX (vbox3), label, FALSE, TRUE, 0);
+        g_signal_connect (label,
+                          "activate-link",
+                          G_CALLBACK (on_activate_link_for_details_label),
+                          shell);
         shell->priv->details3_label = label;
 
         /* --- */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]