diff --git a/Makefile.in b/Makefile.in
index 6e2107687e5782cf5c8cbd17574403c21a105a46..cac0d588907f6e3d2289f4ad422811f16ee329c6 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -53,7 +53,7 @@ start:
 	@tmux send-keys -t ${SESSION_NAME}:pmts.1  \
 	    "python scripts/pmt_rates.py -d ${DETECTOR_ID} -p ${MONITORING_LIGIER_PORT} -i 20 -u 1" Enter
 	@tmux send-keys -t ${SESSION_NAME}:pmts.2  \
-	    "echo nothing to see here" Enter
+	    "python scripts/pmt_hrv.py -d ${DETECTOR_ID} -p ${MONITORING_LIGIER_PORT} -i 20 -u 1" Enter
 	@tmux select-layout even-vertical
 
 	@# Trigger rates
diff --git a/README.md b/README.md
index 6e1d94e61dfcfe28fe448152f5065a3a90ad9c3c..00cdf42b5e22d5d398efecfacb37273fa9669eb9 100644
--- a/README.md
+++ b/README.md
@@ -5,15 +5,19 @@ Monitoring facility for the KM3NeT neutrino detector.
 ## Usage
 
 Check out the configure options with
+
     ./configure --help
 
 then configure the ``Makefile`` with
+
     ./configure
 
 and run
+
     make start
 
 to start the monitoring. If you want to stop it:
+
     make stop
 
 easy.
diff --git a/scripts/pmt_hrv.py b/scripts/pmt_hrv.py
new file mode 100755
index 0000000000000000000000000000000000000000..30b4aac6aa92379f1f5ae84b3b770e6d20e94c8f
--- /dev/null
+++ b/scripts/pmt_hrv.py
@@ -0,0 +1,181 @@
+#!/usr/bin/env python
+# coding=utf-8
+# Filename: pmt_hrv.py
+# Author: Tamas Gal <tgal@km3net.de>
+# vim: ts=4 sw=4 et
+"""
+Monitors HRV flags of PMTs.
+
+Usage:
+    pmt_hrv.py [options]
+    pmt_hrv.py (-h | --help)
+
+Options:
+    -l LIGIER_IP    The IP of the ligier [default: 127.0.0.1].
+    -p LIGIER_PORT  The port of the ligier [default: 5553].
+    -u DU           The DU to monitor [default: 1].
+    -d DET_ID       Detector ID [default: 29].
+    -i INTERVAL     Time interval for one pixel [default: 10].
+    -o PLOT_DIR     The directory to save the plot [default: plots].
+    -h --help       Show this screen.
+
+"""
+from datetime import datetime
+import io
+import os
+from collections import defaultdict
+import threading
+import time
+
+import numpy as np
+import matplotlib
+matplotlib.use('Agg')
+
+import km3pipe as kp
+from km3pipe.io.daq import TMCHData
+import matplotlib.pyplot as plt
+import km3pipe.style as kpst
+kpst.use("km3pipe")
+
+__author__ = "Tamas Gal"
+__email__ = "tgal@km3net.de"
+
+VERSION = "1.0"
+log = kp.logger.logging.getLogger("PMTrates")
+
+
+class PMTHRV(kp.Module):
+    def configure(self):
+        self.detector = self.require("detector")
+        self.du = self.require("du")
+        self.interval = self.get("interval", default=10)
+        self.plot_path = self.get("plot_path", default="plots")
+        self.filename = self.get("filename", default="pmt_hrv.png")
+        self.max_x = 800
+        self.index = 0
+        self.hrv = defaultdict(list)
+        self.hrv_matrix = np.full((18*31, self.max_x), np.nan)
+        self.lock = threading.Lock()
+        self.thread = threading.Thread(target=self.run, args=())
+        self.thread.daemon = True
+        self.thread.start()
+
+    def run(self):
+        interval = self.interval
+        while True:
+            time.sleep(interval)
+            now = datetime.now()
+            self.add_column()
+            self.update_plot()
+            with self.lock:
+                self.rates = defaultdict(list)
+            delta_t = (datetime.now() - now).total_seconds()
+            remaining_t = self.interval - delta_t
+            log.info("Delta t: {} -> waiting for {}s"
+                     .format(delta_t, self.interval - delta_t))
+            if(remaining_t < 0):
+                log.error("Can't keep up with plot production. "
+                          "Increase the interval!")
+                interval = 1
+            else:
+                interval = remaining_t
+
+    def add_column(self):
+        m = np.roll(self.hrv_matrix, -1, 1)
+        y_range = 18*31
+        mean_hrv = np.full(y_range, np.nan)
+        for i in range(y_range):
+            if i not in self.hrv:
+                continue
+            mean_hrv[i] = np.mean(self.hrv[i])
+
+        m[:, self.max_x - 1] = mean_hrv
+        self.hrv_matrix = m
+
+    def update_plot(self):
+        filename = os.path.join(self.plot_path, self.filename)
+        print("Updating plot at {}".format(filename))
+        now = time.time()
+        max_x = self.max_x
+        interval = self.interval
+
+        def xlabel_func(timestamp):
+            return datetime.utcfromtimestamp(timestamp).strftime("%H:%M")
+
+        m = self.hrv_matrix
+        fig, ax = plt.subplots(figsize=(10, 8))
+        ax.imshow(m, origin='lower', interpolation='none')
+        ax.set_title("HRV Ratios (Monitoring Channel) for DetID-{} DU-{}\n"
+                     "PMTs ordered from top to bottom - {}"
+                     .format(self.detector.det_id, self.du, datetime.utcnow()))
+        ax.set_xlabel("UTC time [{}s/px]".format(interval))
+        plt.yticks([i*31 for i in range(18)],
+                   ["Floor {}".format(f) for f in range(1, 19)])
+        xtics_int = range(0, max_x, int(max_x/10))
+        plt.xticks([i for i in xtics_int],
+                   [xlabel_func(now-(max_x-i) * interval) for i in xtics_int])
+        fig.tight_layout()
+        plt.savefig(filename)
+        plt.close('all')
+
+    def process(self, blob):
+        try:
+            tmch_data = TMCHData(io.BytesIO(blob['CHData']))
+        except ValueError:
+            self.log.error("Could not parse binary data. Ignoring...")
+            return blob
+
+        dom_id = tmch_data.dom_id
+
+        if dom_id not in self.detector.doms:
+            return blob
+
+        du, floor, _ = self.detector.doms[dom_id]
+
+        if du != self.du:
+            return blob
+
+        hrv_flags = reversed("{0:b}".format(tmch_data.hrvbmp).zfill(32))
+
+        y_base = (floor - 1) * 31
+
+        for channel_id, hrv_flag in enumerate(hrv_flags):
+            if channel_id > 30:
+                break
+            idx = y_base + kp.hardware.ORDERED_PMT_IDS[channel_id]
+            with self.lock:
+                self.hrv[idx].append(int(hrv_flag))
+
+        return blob
+
+
+def main():
+    from docopt import docopt
+    args = docopt(__doc__, version=VERSION)
+
+    det_id = int(args['-d'])
+    plot_path = args['-o']
+    ligier_ip = args['-l']
+    ligier_port = int(args['-p'])
+    du = int(args['-u'])
+    interval = int(args['-i'])
+
+    detector = kp.hardware.Detector(det_id=det_id)
+
+    pipe = kp.Pipeline(timeit=True)
+    pipe.attach(kp.io.ch.CHPump,
+                host=ligier_ip,
+                port=ligier_port,
+                tags='IO_MONIT',
+                timeout=60*60*24*7,
+                max_queue=2000)
+    pipe.attach(PMTHRV,
+                detector=detector,
+                du=du,
+                interval=interval,
+                plot_path=plot_path)
+    pipe.drain()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/scripts/pmt_rates.py b/scripts/pmt_rates.py
index 0a556d82ddb2de7078479d5a493651315345b416..ee2bc25b6bad9e91d9c4d14d4e17fe6e7950784b 100755
--- a/scripts/pmt_rates.py
+++ b/scripts/pmt_rates.py
@@ -107,10 +107,10 @@ class PMTRates(kp.Module):
         m[m < 5000] = 5000
         fig, ax = plt.subplots(figsize=(10, 8))
         ax.imshow(m, origin='lower', interpolation='none')
-        ax.set_title("Mean PMT Rates (Monitoring Channel) for DU-{} "
+        ax.set_title("Mean PMT Rates (Monitoring Channel) for DetID-{} DU-{} "
                      "- colours from 5kHz to 15kHz\n"
                      "PMTs ordered from top to bottom - {}"
-                     .format(self.du, datetime.utcnow()))
+                     .format(self.detector.det_id, self.du, datetime.utcnow()))
         ax.set_xlabel("UTC time [{}s/px]".format(interval))
         plt.yticks([i*31 for i in range(18)],
                    ["Floor {}".format(f) for f in range(1, 19)])