diff --git a/.gitignore b/.gitignore
index dc8235c..c83e499 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
-blocks
-config.h
+# blocks
+# config.h
 dwmblocks
 sigdwmblocks/sigdwmblocks
 xgetrootname/xgetrootname
diff --git a/blocks/battery.sh b/blocks/battery.sh
new file mode 100755
index 0000000..df98fc8
--- /dev/null
+++ b/blocks/battery.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+ICON=""
+read -r capacity </sys/class/power_supply/BAT0/capacity
+printf "$ICON%s%%" "$capacity"
diff --git a/blocks/battery_button.sh b/blocks/battery_button.sh
new file mode 100755
index 0000000..b6b51ad
--- /dev/null
+++ b/blocks/battery_button.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+read -r rate </sys/class/power_supply/BAT0/current_now
+[ "$rate" = 0 ] && notify-send "Battery fully charged" && exit
+
+read -r ac </sys/class/power_supply/AC/online
+read -r charge_now </sys/class/power_supply/BAT0/charge_now
+
+if [ "$ac" = 1 ] ; then
+    read -r charge_full </sys/class/power_supply/BAT0/charge_full
+    val="$(( charge_full-charge_now ))"
+else
+    val="$charge_now"
+fi
+
+hr="$(( val / rate ))"
+mn="$(( (val * 60) / rate - hr * 60 ))"
+
+case "$hr" in
+    0)
+        case "$mn" in
+            0) notify-send "Battery fully charged" ;;
+            1) notify-send "1 minute remaining" ;;
+            *) notify-send "$mn minutes remaining" ;;
+        esac
+        ;;
+    1)
+        case "$mn" in
+            0) notify-send "1 hour remaining" ;;
+            1) notify-send "1 hour, 1 minute remaining" ;;
+            *) notify-send "1 hour, $mn minutes remaining" ;;
+        esac
+        ;;
+    *)
+        case "$mn" in
+            0) notify-send "$hr hours remaining" ;;
+            1) notify-send "$hr hours, 1 minute remaining" ;;
+            *) notify-send "$hr hours, $mn minutes remaining" ;;
+        esac
+        ;;
+esac
diff --git a/blocks/calendar.sh b/blocks/calendar.sh
new file mode 100755
index 0000000..d651ebf
--- /dev/null
+++ b/blocks/calendar.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+ICON="󰃭"
+printf "$ICON %s" "$(date '+%a, %b %d, %R')"
diff --git a/blocks/cpu_temp.sh b/blocks/cpu_temp.sh
new file mode 100755
index 0000000..8159ea1
--- /dev/null
+++ b/blocks/cpu_temp.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+ICONn="" # icon for normal temperatures
+ICONc="
" # icon for critical temperatures
+
+crit=70 # critical temperature
+
+read -r temp </sys/class/thermal/thermal_zone0/temp
+temp="${temp%???}"
+
+if [ "$temp" -lt "$crit" ] ; then
+    printf "$ICONn%s°C" "$temp"
+else
+    printf "$ICONc%s°C" "$temp"
+fi
diff --git a/blocks/cpu_temp_button.sh b/blocks/cpu_temp_button.sh
new file mode 100755
index 0000000..29624c7
--- /dev/null
+++ b/blocks/cpu_temp_button.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+case "$1" in
+    1) exec "$TERMINAL" -e htop -s PERCENT_CPU ;;
+    2) exec "$TERMINAL" -e htop ;;
+    3) exec "$TERMINAL" -e htop -s PERCENT_MEM ;;
+esac
diff --git a/blocks/keyboard.sh b/blocks/keyboard.sh
new file mode 100755
index 0000000..6004340
--- /dev/null
+++ b/blocks/keyboard.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+icon=""
+kb="$(xkb-switch)" || exit 1
+
+printf "%s %s" "$icon" "$kb"
diff --git a/blocks/volume.pipewire.sh b/blocks/volume.pipewire.sh
new file mode 100755
index 0000000..b4e6c8f
--- /dev/null
+++ b/blocks/volume.pipewire.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+# (for pipewire users)
+# This script parses the output of `pactl list sinks' to find volume and mute
+# status of the default audio sink and whether headphones are plugged in or not
+# Also see ../daemons/pulse_daemon.sh
+sink="$(pactl info | awk '$1 == "Default" && $2 == "Sink:" {print $3}')"
+[ -n "$sink" ] || exit
+pactl list sinks | awk -v sink="$sink" '
+    BEGIN {
+        ICONsn = "\x0c\x0b" # headphone unplugged, not muted
+        ICONsm = "\x0d\x0b" # headphone unplugged, muted
+        ICONhn = "\x0c\x0b" # headphone plugged in, not muted
+        ICONhm = "\x0d\x0b" # headphone plugged in, muted
+    }
+    f {
+        if ($1 == "Mute:" && $2 == "yes") {
+            m = 1
+        } else if ($1 == "Volume:") {
+            if ($3 == $10) {
+                vb = $5
+            } else {
+                vl = $5
+                vr = $12
+            }
+        } else if ($1 == "Active" && $2 == "Port:") {
+            if (tolower($3) ~ /headphone/)
+                h = 1
+            exit
+        }
+        next
+    }
+    $1 == "Name:" && $2 == sink {
+        f = 1
+    }
+    END {
+        if (f) {
+            printf "%s", h ? (m ? ICONhm : ICONhn) : (m ? ICONsm : ICONsn)
+            if (vb)
+                print vb
+            else
+                printf "L%s R%s\n", vl, vr
+        }
+    }
+'
diff --git a/blocks/volume.sh b/blocks/volume.sh
new file mode 100755
index 0000000..b74139d
--- /dev/null
+++ b/blocks/volume.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+# (for pulseaudio users)
+# This script parses the output of `pacmd list-sinks' to find volume and mute
+# status of the default audio sink and whether headphones are plugged in or not
+# Also see ../daemons/pulse_daemon.sh
+pacmd list-sinks | awk '
+    BEGIN {
+        ICONsn = "\x0c\x0b" # headphone unplugged, not muted
+        ICONsm = "\x0d\x0b" # headphone unplugged, muted
+        ICONhn = "\x0c\x0b" # headphone plugged in, not muted
+        ICONhm = "\x0d\x0b" # headphone plugged in, muted
+    }
+    f {
+        if ($1 == "muted:" && $2 == "yes") {
+            m = 1
+        } else if ($1 == "volume:") {
+            if ($3 == $10) {
+                vb = $5
+            } else {
+                vl = $5
+                vr = $12
+            }
+        } else if ($1 == "active" && $2 == "port:") {
+            if (tolower($3) ~ /headphone/)
+                h = 1
+            exit
+        }
+        next
+    }
+    $1 == "*" && $2 == "index:" {
+        f = 1
+    }
+    END {
+        if (f) {
+            printf "%s", h ? (m ? ICONhm : ICONhn) : (m ? ICONsm : ICONsn)
+            if (vb)
+                print vb
+            else
+                printf "L%s R%s\n", vl, vr
+        }
+    }
+'
diff --git a/blocks/volume_button.sh b/blocks/volume_button.sh
new file mode 100755
index 0000000..b0f58d4
--- /dev/null
+++ b/blocks/volume_button.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+case "$1" in
+    1) pactl set-sink-mute @DEFAULT_SINK@ toggle ;;
+    3) pactl set-sink-volume @DEFAULT_SINK@ 50% ;;
+esac
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..70f4531
--- /dev/null
+++ b/config.h
@@ -0,0 +1,52 @@
+/* macro for conveniently specifying pathu and pathc below */
+#define PATH(name)                      "/home/andy/.local/src/dwmblocks/blocks/"name
+
+/* buffer size for capturing output of the programs used for updating blocks */
+#define CMDOUTLENGTH                    50
+
+/* DELIMITERENDCHAR must be less than 32.
+ * At max, DELIMITERENDCHAR - 1 number of clickable blocks are allowed.
+ * Raw characters larger than DELIMITERENDCHAR and smaller than ' ' in ASCII
+   character set can be used for signaling color change in status.
+ * The character corresponding to DELIMITERENDCHAR + 1 ('\x0b' when
+   DELIMITERENDCHAR is 10) will switch the active colorscheme to the first one
+   defined in colors array in dwm's config.h and so on.
+ * If you wish to change DELIMITERENDCHAR, don't forget to update its value in
+   dwm.c and color codes in your pathu programs. */
+#define DELIMITERENDCHAR                10
+
+/* delimiter specified as an array of characters
+ * (don't remove DELIMITERENDCHAR at the end) */
+static const char delimiter[] = { ' ', '|', ' ', DELIMITERENDCHAR };
+
+#include "block.h"
+
+/* If interval of a block is set to 0, the block will only be updated once at
+   startup.
+ * If interval is set to a negative value, the block will never be updated in
+   the main loop.
+ * Set pathc to NULL if clickability is not required for the block.
+ * Set signal to 0 if both clickability and signaling are not required for the
+   block.
+ * Signal must be less than DELIMITERENDCHAR for clickable blocks.
+ * If multiple signals are pending, then the lowest numbered one will be
+   delivered first. */
+
+/* pathu - path of the program whose output is used as status text of the block
+           (output of the program should be a single line of text with at most
+            one newline character at the end)
+ * pathc - path of the program used for handling clicks on the block */
+
+/* 1 interval = INTERVALs seconds, INTERVALn nanoseconds */
+#define INTERVALs                       1
+#define INTERVALn                       0
+
+static Block blocks[] = {
+/*      pathu                           pathc                           interval        signal */
+        { PATH("keyboard.sh"),          NULL,                           1,              0},
+        { PATH("volume.pipewire.sh"),   PATH("volume_button.sh"),       0,              2},
+        // { PATH("cpu_temp.sh"),       PATH("cpu_temp_button.sh"),     1,              4},
+        // { PATH("battery.sh"),        PATH("battery_button.sh"),      30,             3},
+        { PATH("calendar.sh"),          NULL,                           30,             1},
+        { NULL } /* just to mark the end of the array */
+};
diff --git a/daemons/pulse_daemon.sh b/daemons/pulse_daemon.sh
index 222099b..c7fd24a 100755
--- a/daemons/pulse_daemon.sh
+++ b/daemons/pulse_daemon.sh
@@ -4,6 +4,6 @@
 pactl subscribe |
     while IFS='' read -r output ; do
         case "$output" in
-            *" sink "*) sigdwmblocks 1 ;;
+            *" sink "*) sigdwmblocks 2 ;;
         esac
     done
