diff --git a/.gitignore b/.gitignore
index 0b217902931bdecf67e4d9aad3e4071c4a45154b..733e87c5f324a13caae2daccddec5e2e01ecaad6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ Makefile
 data/
 __pycache__
 *.pyc
+plots/
diff --git a/Makefile.in b/Makefile.in
index 5d3dc5d701f81eb8ffe161e925d600e445e512b4..6e2107687e5782cf5c8cbd17574403c21a105a46 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -41,9 +41,19 @@ start:
 	@tmux new-window -n doms -t ${SESSION_NAME}
 	@tmux split-window -v -t ${SESSION_NAME}:doms
 	@tmux send-keys -t ${SESSION_NAME}:doms.1  \
-	    "python scripts/dom_activity.py" Enter
+	    "python scripts/dom_activity.py -d ${DETECTOR_ID} -p ${MONITORING_LIGIER_PORT}" Enter
 	@tmux send-keys -t ${SESSION_NAME}:doms.2  \
-	    "python scripts/dom_rates.py" Enter
+	    "python scripts/dom_rates.py -d ${DETECTOR_ID} -p ${MONITORING_LIGIER_PORT}" Enter
+	@tmux select-layout even-vertical
+
+	@# PMT rates and HRV
+	@#
+	@tmux new-window -n pmts -t ${SESSION_NAME}
+	@tmux split-window -v -t ${SESSION_NAME}:pmts
+	@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
 	@tmux select-layout even-vertical
 
 	@# Trigger rates
@@ -51,7 +61,7 @@ start:
 	@tmux new-window -n trigger -t ${SESSION_NAME}
 	@tmux split-window -v -t ${SESSION_NAME}:trigger
 	@tmux send-keys -t ${SESSION_NAME}:trigger.1  \
-	    "python scripts/trigger_rates.py" Enter
+	    "python scripts/trigger_rates.py -p ${MONITORING_LIGIER_PORT}" Enter
 	@tmux select-layout even-vertical
 
 
diff --git a/configure b/configure
index 034a1e5ef4e24934b319057c5f3c68fcdf0c9442..1d292ff406e227bdbdbe091abea336e29893d157 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,6 @@
 #!/bin/sh
 
+detector_id=29
 daq_ligier_ip="192.168.0.110"
 daq_ligier_port=5553
 monitoring_ligier_ip="127.0.0.1"
@@ -20,6 +21,10 @@ show_km3mon_banner()
 
 for arg in "$@"; do
     case "$arg" in
+    --detector-id=*)
+        detector_id=`echo $arg | sed 's/--detector-id=//'`
+        ;;
+
     --daq-ligier-ip=*)
         daq_ligier_ip=`echo $arg | sed 's/--daq-ligier-ip=//'`
         ;;
@@ -45,6 +50,7 @@ for arg in "$@"; do
         echo 'Usage:  ./configure [options]'
         echo
         echo '  OPTION                    DESCRIPTION                    DEFAULT'
+        echo "  --detector-id             Detector ID                    ${detector_id}"
         echo "  --daq-ligier-ip           DAQ Ligier                     ${daq_ligier_ip}"
         echo "  --daq-ligier-port         Port of the DAQ Ligier         ${daq_ligier_port}"
         echo "  --monitoring-ligier-port  Port of the monitoring Ligier  ${monitoring_ligier_port}"
@@ -57,7 +63,8 @@ for arg in "$@"; do
     esac
 done
 
-echo "DAQ_LIGIER_IP = ${daq_ligier_ip}" > Makefile
+echo "DETECTOR_ID = ${detector_id}" > Makefile
+echo "DAQ_LIGIER_IP = ${daq_ligier_ip}" >> Makefile
 echo "DAQ_LIGIER_PORT = ${daq_ligier_port}" >> Makefile
 echo "MONITORING_LIGIER_PORT = ${monitoring_ligier_port}" >> Makefile
 echo "WEBSERVER_PORT = ${webserver_port}" >> Makefile
@@ -66,6 +73,7 @@ echo "SESSION_NAME = ${tmux_session_name}" >> Makefile
 cat Makefile.in >> Makefile
 
 show_km3mon_banner
+echo "Detector ID:        ${detector_id}"
 echo "DAQ Ligier:         ${daq_ligier_ip}:${daq_ligier_port}"
 echo "Monitoring Ligier:  ${monitoring_ligier_ip}:${monitoring_ligier_port}"
 echo "Webserver:          0.0.0.0:${webserver_port}"
diff --git a/scripts/dom_activity.py b/scripts/dom_activity.py
index 975d2ed2ca2d16bf4e2f2f48630a2351ca0dcaba..1ebbd6a4b2f134ca43a947f56d1471486c16b15a 100755
--- a/scripts/dom_activity.py
+++ b/scripts/dom_activity.py
@@ -26,6 +26,9 @@ from io import BytesIO
 import os
 import time
 
+import matplotlib
+matplotlib.use("Agg")
+
 import km3pipe as kp
 import km3pipe.style
 from km3modules.plot import plot_dom_parameters
diff --git a/scripts/dom_rates.py b/scripts/dom_rates.py
index ff3d0f7a014f10f8ee4f37224aa461dad68f32e0..2c74468213bca821befcd5a5f6063472f86c6b1a 100755
--- a/scripts/dom_rates.py
+++ b/scripts/dom_rates.py
@@ -24,6 +24,8 @@ from io import BytesIO
 import os
 
 import numpy as np
+import matplotlib
+matplotlib.use("Agg")
 
 import km3pipe as kp
 import km3pipe.style
diff --git a/scripts/pmt_rates.py b/scripts/pmt_rates.py
new file mode 100755
index 0000000000000000000000000000000000000000..d9317865fdf065bdd5a026dc03ae51a356112a43
--- /dev/null
+++ b/scripts/pmt_rates.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python
+# coding=utf-8
+# Filename: pmt_rates.py
+# Author: Tamas Gal <tgal@km3net.de>
+# vim: ts=4 sw=4 et
+"""
+Monitors PMT rates.
+
+Usage:
+    pmt_rates.py [options]
+    pmt_rates.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: www/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 PMTRates(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="www/plots")
+        self.filename = self.get("filename", default="pmtrates.png")
+        self.max_x = 800
+        self.index = 0
+        self.rates = defaultdict(list)
+        self.rates_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.rates_matrix, -1, 1)
+        y_range = 18*31
+        mean_rates = np.full(y_range, np.nan)
+        for i in range(y_range):
+            if i not in self.rates:
+                continue
+            mean_rates[i] = np.mean(self.rates[i])
+
+        m[:, self.max_x - 1] = mean_rates
+        self.rates_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.rates_matrix
+        m[m > 15000] = 15000
+        m[m < 5000] = 5000
+        fig, ax = plt.subplots(figsize=(10, 8))
+        ax.imshow(m, origin='lower', interpolation='none')
+        ax.set_title("Mean PMT Rates for DU-{} (colours from 5kHz to 15kHz)\n"
+                     "(PMTs ordered from top to bottom) - {}"
+                     .format(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):
+        tmch_data = TMCHData(io.BytesIO(blob['CHData']))
+        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
+
+        y_base = (floor - 1) * 31
+
+        for channel_id, rate in enumerate(tmch_data.pmt_rates):
+            idx = y_base + kp.hardware.ORDERED_PMT_IDS[channel_id]
+            with self.lock:
+                self.rates[idx].append(rate)
+
+        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(PMTRates,
+                detector=detector,
+                du=du,
+                interval=interval,
+                plot_path=plot_path)
+    pipe.drain()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/scripts/trigger_rates.py b/scripts/trigger_rates.py
old mode 100644
new mode 100755
index 477a8a68bda2d75d493295dc51aee38e29aa9116..fc4c0928606116fc7b25d671e9cc6fbe2c156d4e
--- a/scripts/trigger_rates.py
+++ b/scripts/trigger_rates.py
@@ -28,6 +28,8 @@ import shutil
 import time
 import threading
 
+import matplotlib
+matplotlib.use("Agg")
 import matplotlib.pyplot as plt
 import matplotlib.dates as md