-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·201 lines (186 loc) · 8.76 KB
/
Copy pathinstall.sh
File metadata and controls
executable file
·201 lines (186 loc) · 8.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/env bash
# Install/refresh PiClock on the Raspberry Pi.
#
# Creates the dedicated `clocker` service account, deploys the app to
# /opt/piclock owned by that account, builds its venv, and installs + enables
# the systemd service. Run with sudo from a checkout of this repo:
#
# sudo scripts/install.sh # core appliance only
# sudo scripts/install.sh --homekit # also enable HomeKit (v2)
# sudo scripts/install.sh --schedule # also enable day/night timers
# sudo scripts/install.sh --reboot # also enable monthly reboot
# sudo scripts/install.sh --homekit --schedule # combine as needed
#
# --homekit additionally installs the HomeKit deps into the venv and drops in
# the systemd override that turns the integration on. --schedule drops in the
# override that turns on the in-app day/night schedule (blank the display by
# day, show it at the wake brightness in the evening, dim it for night; times
# via PICLOCK_OFF_AT/ON_AT/DIM_AT — see systemd/piclock-schedule.conf). The
# schedule runs inside the app, so there are no timers to install and the
# correct state is re-asserted automatically on every start. --reboot enables a
# monthly midday reboot that returns the appliance to a known-good state.
# Re-running WITHOUT a flag undoes that flag's changes, so each flag toggles
# its feature. (One nuance: dropping --homekit removes the drop-in that
# activates the integration but leaves the HomeKit packages in the venv; they
# are inert.)
#
# Idempotent; safe to re-run after pulling new code.
set -euo pipefail
SERVICE_USER="clocker"
APP_DIR="/opt/piclock"
SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
UNIT_DST="/etc/systemd/system/piclock.service"
DROPIN_DIR="/etc/systemd/system/piclock.service.d"
HOMEKIT_DROPIN="$DROPIN_DIR/homekit.conf"
SCHEDULE_DROPIN="$DROPIN_DIR/schedule.conf"
# Units backing the optional scheduled reboot (timer + the oneshot it fires).
REBOOT_UNITS="piclock-reboot.timer piclock-reboot.service"
ENABLE_HOMEKIT=0
ENABLE_SCHEDULE=0
ENABLE_REBOOT=0
for arg in "$@"; do
case "$arg" in
--homekit) ENABLE_HOMEKIT=1 ;;
--schedule) ENABLE_SCHEDULE=1 ;;
--reboot) ENABLE_REBOOT=1 ;;
*) echo "Unknown argument: $arg (supported: --homekit, --schedule, --reboot)" >&2; exit 1 ;;
esac
done
if [ "$(id -u)" -ne 0 ]; then
echo "This script must be run as root (use sudo)." >&2
exit 1
fi
echo ">> Source: $SRC_DIR"
echo ">> Target: $APP_DIR (service user: $SERVICE_USER)"
# 1. Service account: a system user with no login shell, home = the app dir.
# It needs the i2c group to reach /dev/i2c-1 and nothing else; no sudo.
if ! getent group i2c >/dev/null; then
echo "No 'i2c' group: enable I2C first (sudo raspi-config nonint do_i2c 0)." >&2
exit 1
fi
# The group alone doesn't prove the bus is on (it exists even with I2C
# disabled); require the device node the service actually opens.
if [ ! -e /dev/i2c-1 ]; then
echo "No /dev/i2c-1: enable I2C first (sudo raspi-config nonint do_i2c 0)." >&2
exit 1
fi
if ! id "$SERVICE_USER" >/dev/null 2>&1; then
echo ">> Creating service account '$SERVICE_USER'"
useradd --system --home-dir "$APP_DIR" --shell /usr/sbin/nologin "$SERVICE_USER"
fi
usermod -aG i2c "$SERVICE_USER"
# The clockctl group gates the control socket (one-off CLI commands via
# piclockctl). The service account must be a member so it can chgrp the socket
# it creates; add any human users who should be allowed to command the display
# with `usermod -aG clockctl <user>` (they must log out/in to pick it up).
if ! getent group clockctl >/dev/null; then
echo ">> Creating 'clockctl' group (members may run piclockctl)"
groupadd --system clockctl
fi
usermod -aG clockctl "$SERVICE_USER"
# 2. Deploy only the runtime bundle and hand ownership to clocker. The source
# checkout may contain docs, tests, CI, operator notes, or local-only files;
# /opt/piclock gets only what the service needs to run.
echo ">> Deploying code to $APP_DIR"
mkdir -p "$APP_DIR"
rsync -a --delete \
--delete-excluded \
--filter 'P /venv/***' \
--filter 'P /.cache/***' \
--exclude '__pycache__/' \
--exclude '*.pyc' \
--include '/main.py' \
--include '/requirements.txt' \
--include '/requirements-homekit.txt' \
--include '/piclock/***' \
--exclude '*' \
"$SRC_DIR/" "$APP_DIR/"
chown -R "$SERVICE_USER:$SERVICE_USER" "$APP_DIR"
# 3. Build the venv and install runtime deps as the service account.
echo ">> Building venv and installing dependencies"
if [ ! -d "$APP_DIR/venv" ]; then
sudo -u "$SERVICE_USER" python3 -m venv "$APP_DIR/venv"
fi
sudo -u "$SERVICE_USER" "$APP_DIR/venv/bin/pip" install -r "$APP_DIR/requirements.txt"
if [ "$ENABLE_HOMEKIT" -eq 1 ]; then
# The zeroconf pin is selected per-architecture by PEP 508 markers in
# requirements-homekit.txt; pip evaluates them, so no board flag is needed.
# This line is purely informational (armv6 = Zero W → legacy pin).
echo ">> Installing HomeKit dependencies (arch: $(uname -m))"
sudo -u "$SERVICE_USER" "$APP_DIR/venv/bin/pip" install -r "$APP_DIR/requirements-homekit.txt"
fi
# 4. Install the systemd unit verbatim (it already targets clocker + /opt/piclock),
# and the piclockctl client the unit's control socket serves.
echo ">> Installing systemd unit to $UNIT_DST"
install -m 0644 "$SRC_DIR/systemd/piclock.service" "$UNIT_DST"
echo ">> Installing piclockctl to /usr/local/bin"
install -m 0755 "$SRC_DIR/scripts/piclockctl" /usr/local/bin/piclockctl
# 4b. HomeKit is enabled via a drop-in override (StateDirectory + PICLOCK_HOMEKIT
# env), kept out of the base unit. With --homekit install it; otherwise make
# sure any prior one is gone, so the flag fully toggles the integration.
if [ "$ENABLE_HOMEKIT" -eq 1 ]; then
echo ">> Enabling HomeKit drop-in at $HOMEKIT_DROPIN"
install -d -m 0755 "$DROPIN_DIR"
install -m 0644 "$SRC_DIR/systemd/piclock-homekit.conf" "$HOMEKIT_DROPIN"
elif [ -f "$HOMEKIT_DROPIN" ]; then
echo ">> Removing HomeKit drop-in (run without --homekit)"
rm -f "$HOMEKIT_DROPIN"
fi
# 4c. Day/night schedule: enabled via a drop-in override (PICLOCK_SCHEDULE env,
# plus optional PICLOCK_OFF_AT/ON_AT/DIM_AT times). The schedule itself runs
# inside the app; the service stays up either way, so HomeKit/Siri can still
# override on demand. With --schedule install the drop-in; otherwise make
# sure any prior one is gone, so the flag fully toggles the feature.
if [ "$ENABLE_SCHEDULE" -eq 1 ]; then
echo ">> Enabling day/night schedule drop-in at $SCHEDULE_DROPIN"
install -d -m 0755 "$DROPIN_DIR"
install -m 0644 "$SRC_DIR/systemd/piclock-schedule.conf" "$SCHEDULE_DROPIN"
elif [ -f "$SCHEDULE_DROPIN" ]; then
echo ">> Removing day/night schedule drop-in (run without --schedule)"
rm -f "$SCHEDULE_DROPIN"
fi
# 4d. Scheduled reboot: a monthly timer that reboots the Pi at midday (inside the
# display off-window) to return to a known-good state. With --reboot install
# the units; otherwise disable + remove any previously installed ones.
if [ "$ENABLE_REBOOT" -eq 1 ]; then
echo ">> Installing scheduled-reboot units"
for u in $REBOOT_UNITS; do
install -m 0644 "$SRC_DIR/systemd/$u" "/etc/systemd/system/$u"
done
else
systemctl disable --now piclock-reboot.timer 2>/dev/null || true
for u in $REBOOT_UNITS; do
rm -f "/etc/systemd/system/$u"
done
fi
# 5. Install the timesyncd drop-in. It is a no-op by default (keeps the stock
# NTP pool, which is all the clock needs); edit it to pin a custom/LAN NTP
# server. See systemd/timesyncd-piclock.conf.
echo ">> Installing timesyncd drop-in (no-op default; edit to pin an NTP server)"
install -d -m 0755 /etc/systemd/timesyncd.conf.d
install -m 0644 "$SRC_DIR/systemd/timesyncd-piclock.conf" \
/etc/systemd/timesyncd.conf.d/10-piclock.conf
systemctl restart systemd-timesyncd
# 6. Enable + (re)start.
echo ">> Enabling and (re)starting the service"
systemctl daemon-reload
systemctl enable piclock
systemctl restart piclock
if [ "$ENABLE_REBOOT" -eq 1 ]; then
echo ">> Enabling monthly reboot timer"
systemctl enable --now piclock-reboot.timer
fi
echo ">> Done. Recent status:"
systemctl --no-pager --lines=10 status piclock || true
echo ">> Follow logs with: journalctl -u piclock -f"
if [ "$ENABLE_HOMEKIT" -eq 1 ]; then
echo ">> HomeKit enabled. Find the pairing PIN in the log, then add the"
echo " 'Clock' accessory in the Home app: journalctl -u piclock | grep 'pairing PIN'"
fi
if [ "$ENABLE_SCHEDULE" -eq 1 ]; then
echo ">> Day/night schedule enabled (in-app). Watch edges apply with:"
echo " journalctl -u piclock -f | grep 'schedule:'"
fi
if [ "$ENABLE_REBOOT" -eq 1 ]; then
echo ">> Monthly reboot enabled. Next run: systemctl list-timers piclock-reboot.timer"
fi