Merge branch 'BirdNET-pi-main' into More-Misc-Fixes-and-Enhancements_mcguire

This commit is contained in:
jaredb7
2023-04-08 11:07:19 +10:00
12 changed files with 2146 additions and 40 deletions
+57 -36
View File
@@ -268,9 +268,10 @@ if (file_exists('./scripts/thisrun.txt')) {
$newconfig = parse_ini_file('./scripts/firstrun.ini');
}
?>
<h2>Advanced Settings</h2>
<div class="brbanner"><h1>Advanced Settings</h1></div><br>
<form action="" method="GET">
<label>Privacy Threshold: </label><br>
<table class="settingstable"><tr><td>
<h2>Privacy Threshold</h2>
<div class="slidecontainer">
<input name="privacy_threshold" type="range" min="0" max="3" value="<?php print($newconfig['PRIVACY_THRESHOLD']);?>" class="slider" id="privacy_threshold">
<p>Value: <span id="threshold_value"></span>%</p>
@@ -285,7 +286,6 @@ if (file_exists('./scripts/thisrun.txt')) {
output.innerHTML = this.value;
document.getElementById("predictionCount").innerHTML = parseInt((this.value * <?php echo $count; ?>)/100);
}
//Keep track of how many new input fields were added
var number_of_new_rtsp_urls_added = 0;
//Function to insert new input fields
@@ -339,18 +339,43 @@ if (file_exists('./scripts/thisrun.txt')) {
}
</script>
<p>If a Human is predicted anywhere among the top <span id="predictionCount"><?php echo $newconfig['PRIVACY_THRESHOLD'] == 0 ? "threshold % of" : intval(($newconfig['PRIVACY_THRESHOLD'] * $count)/100); ?></span> predictions, the sample will be considered of human origin and no data will be collected. Start with 1% and move up as needed.</p>
<label>Full Disk Behavior: </label>
</td></tr></table><br>
<table class="settingstable"><tr><td>
<h2>Full Disk Behaviour</h2>
<label for="purge">
<input name="full_disk" type="radio" id="purge" value="purge" <?php if (strcmp($newconfig['FULL_DISK'], "purge") == 0) { echo "checked"; }?>>Purge</label>
<label for="keep">
<input name="full_disk" type="radio" id="keep" value="keep" <?php if (strcmp($newconfig['FULL_DISK'], "keep") == 0) { echo "checked"; }?>>Keep</label>
<p>When the disk becomes full, you can choose to 'purge' old files to make room for new ones or 'keep' your data and stop all services instead.<br>Note: you can exclude specific files from 'purge' on the Recordings page.</p>
</td></tr></table><br>
<table class="settingstable"><tr><td>
<h2>Audio Settings</h2>
<label for="rec_card">Audio Card: </label>
<input name="rec_card" type="text" value="<?php print($newconfig['REC_CARD']);?>" required/><br>
<p>Set Audio Card to 'default' to use PulseAudio (always recommended), or an ALSA recognized sound card device from the output of `arecord -L`. Choose the `dsnoop` device if it is available</p>
<label for="channels">Audio Channels: </label>
<input name="channels" type="number" min="1" max="32" step="1" value="<?php print($newconfig['CHANNELS']);?>" required/><br>
<p>Set Channels to the number of channels supported by your sound card. 32 max.</p>
<label for="recording_length">Recording Length: </label>
<input name="recording_length" oninput="document.getElementsByName('extraction_length')[0].setAttribute('max', this.value);" type="number" min="3" max="60" step="1" value="<?php print($newconfig['RECORDING_LENGTH']);?>" required/><br>
<p>Set Recording Length in seconds between 6 and 60. Multiples of 3 are recommended, as BirdNET analyzes in 3-second chunks.</p>
<label for="extraction_length">Extraction Length: </label>
<input name="extraction_length" oninput="this.setAttribute('max', document.getElementsByName('recording_length')[0].value);" type="number" min="3" value="<?php print($newconfig['EXTRACTION_LENGTH']);?>" /><br>
<p>Set Extraction Length to something less than your Recording Length. Min=3 Max=Recording Length</p>
<label for="audiofmt">Extractions Audio Format</label>
<select name="audiofmt">
<option selected="<?php print($newconfig['AUDIOFMT']);?>"><?php print($newconfig['AUDIOFMT']);?></option>
<?php
$formats = array("8svx", "aif", "aifc", "aiff", "aiffc", "al", "amb", "amr-nb", "amr-wb", "anb", "au", "avr", "awb", "caf", "cdda", "cdr", "cvs", "cvsd", "cvu", "dat", "dvms", "f32", "f4", "f64", "f8", "fap", "flac", "fssd", "gsm", "gsrt", "hcom", "htk", "ima", "ircam", "la", "lpc", "lpc10", "lu", "mat", "mat4", "mat5", "maud", "mp2", "mp3", "nist", "ogg", "paf", "prc", "pvf", "raw", "s1", "s16", "s2", "s24", "s3", "s32", "s4", "s8", "sb", "sd2", "sds", "sf", "sl", "sln", "smp", "snd", "sndfile", "sndr", "sndt", "sou", "sox", "sph", "sw", "txw", "u1", "u16", "u2", "u24", "u3", "u32", "u4", "u8", "ub", "ul", "uw", "vms", "voc", "vorbis", "vox", "w64", "wav", "wavpcm", "wv", "wve", "xa", "xi");
foreach($formats as $format){
echo "<option value='$format'>$format</option>";
}
?>
</select>
<br><br>
<label id="rtsp_stream_input_label" for="rtsp_stream">RTSP Stream: </label>
<br>
<input style="display: none;" name="rtsp_stream" type="url" value="">
@@ -379,34 +404,25 @@ if (file_exists('./scripts/thisrun.txt')) {
?>
<div id="newrtspstream_button_container">
<br>
<span id="newrtspstream" onclick="addNewrtspInput();">add</span><br>
<span id="newrtspstream" onclick="addNewrtspInput();">Add</span><br>
</div>
<p>If you place an RTSP stream URL here, BirdNET-Pi will use that as its audio source.<br>Multiple streams are allowed but may have a impact on rPi performance.<br>Analyze ffmpeg CPU/Memory usage with <b>top</b> or <b>htop</b> if necessary.<br>To remove all and use the soundcard again, just delete the RTSP entries and click Save at the bottom.</p>
<label for="recording_length">Recording Length: </label>
<input name="recording_length" oninput="document.getElementsByName('extraction_length')[0].setAttribute('max', this.value);" type="number" min="3" max="60" step="1" value="<?php print($newconfig['RECORDING_LENGTH']);?>" required/><br>
<p>Set Recording Length in seconds between 6 and 60. Multiples of 3 are recommended, as BirdNET analyzes in 3-second chunks.</p>
<label for="extraction_length">Extraction Length: </label>
<input name="extraction_length" oninput="this.setAttribute('max', document.getElementsByName('recording_length')[0].value);" type="number" min="3" value="<?php print($newconfig['EXTRACTION_LENGTH']);?>" /><br>
<p>Set Extraction Length to something less than your Recording Length. Min=3 Max=Recording Length</p>
<label for="audiofmt">Extractions Audio Format</label>
<select name="audiofmt">
<option selected="<?php print($newconfig['AUDIOFMT']);?>"><?php print($newconfig['AUDIOFMT']);?></option>
<?php
$formats = array("8svx", "aif", "aifc", "aiff", "aiffc", "al", "amb", "amr-nb", "amr-wb", "anb", "au", "avr", "awb", "caf", "cdda", "cdr", "cvs", "cvsd", "cvu", "dat", "dvms", "f32", "f4", "f64", "f8", "fap", "flac", "fssd", "gsm", "gsrt", "hcom", "htk", "ima", "ircam", "la", "lpc", "lpc10", "lu", "mat", "mat4", "mat5", "maud", "mp2", "mp3", "nist", "ogg", "paf", "prc", "pvf", "raw", "s1", "s16", "s2", "s24", "s3", "s32", "s4", "s8", "sb", "sd2", "sds", "sf", "sl", "sln", "smp", "snd", "sndfile", "sndr", "sndt", "sou", "sox", "sph", "sw", "txw", "u1", "u16", "u2", "u24", "u3", "u32", "u4", "u8", "ub", "ul", "uw", "vms", "voc", "vorbis", "vox", "w64", "wav", "wavpcm", "wv", "wve", "xa", "xi");
foreach($formats as $format){
echo "<option value='$format'>$format</option>";
}
?>
</select>
<h3>BirdNET-Pi Password</h3>
</td></tr></table><br>
<table class="settingstable"><tr><td>
<h2>BirdNET-Pi Password</h2>
<p>This password will protect your "Tools" page and "Live Audio" stream.</p>
<label for="caddy_pwd">Password: </label>
<input style="width:40ch" name="caddy_pwd" id="caddy_pwd" type="password" pattern="[A-Za-z0-9]+" title="Password must be alphanumeric (A-Z, 0-9)" value="<?php print($newconfig['CADDY_PWD']);?>" /><span id="showpassword" onmouseover="document.getElementById('caddy_pwd').type='text';" onmouseout="document.getElementById('caddy_pwd').type='password';">show</span><br>
<h3>Custom URL</h3>
</td></tr></table><br>
<table class="settingstable"><tr><td>
<h2>Custom URL</h2>
<p>When you update the URL below, the web server will reload, so be sure to wait at least 30 seconds and then go to your new URL.</p>
<label for="birdnetpi_url">BirdNET-Pi URL: </label>
<input style="width:40ch;" name="birdnetpi_url" type="url" value="<?php print($newconfig['BIRDNETPI_URL']);?>" /><br>
<p>The BirdNET-Pi URL is how the main page will be reached. If you want your installation to respond to an IP address, place that here, but be sure to indicate "<i>http://</i>".<br>Example for IP: <i>http://192.168.0.109</i><br>Example if you own your own domain: <i>https://virginia.birdnetpi.com</i></p>
</td></tr></table><br>
<table class="settingstable"><tr><td>
<h2>Options</h2>
<label for="silence_update_indicator">Silence Update Indicator: </label>
<input type="checkbox" name="silence_update_indicator" <?php if($newconfig['SILENCE_UPDATE_INDICATOR'] == 1) { echo "checked"; };?> ><br>
<p>This allows you to quiet the display of how many commits your installation is behind by relative to the Github repo. This number appears next to "Tools" when you're 50 or more commits behind.</p>
@@ -414,9 +430,10 @@ foreach($formats as $format){
<label for="raw_spectrogram">Minimalist Spectrograms: </label>
<input type="checkbox" name="raw_spectrogram" <?php if($newconfig['RAW_SPECTROGRAM'] == 1) { echo "checked"; };?> ><br>
<p>This allows you to remove the axes and labels of the spectrograms that are generated by Sox for each detection for a cleaner appearance.</p>
</td></tr></table><br>
<br>
<table class="settingstable"><tr><td>
<h2>Custom Image</h2>
<label for="custom_image">Custom Image Absolute Path: </label>
<input name="custom_image" type="text" value="<?php print($newconfig['CUSTOM_IMAGE']);?>"/><br>
@@ -424,8 +441,10 @@ foreach($formats as $format){
<input name="custom_image_label" type="text" value="<?php print($newconfig['CUSTOM_IMAGE_TITLE']);?>"/><br>
<p>These allow you to show a custom image on the Overview page of your BirdNET-Pi. This can be used to show a dynamically updating picture of your garden, for example.</p>
</td></tr></table><br>
<h3>BirdNET-Lite Settings</h3>
<table class="settingstable"><tr><td>
<h2>BirdNET-Lite Settings</h2>
<p>
<label for="overlap">Overlap: </label>
@@ -442,15 +461,17 @@ foreach($formats as $format){
<input name="sensitivity" type="number" min="0.5" max="1.5" step="0.01" value="<?php print($newconfig['SENSITIVITY']);?>" required/><br>
&nbsp;&nbsp;&nbsp;&nbsp;Min=0.5, Max=1.5
</p>
</td></tr></table><br>
<h3>Accessibility Settings</h3>
<table class="settingstable"><tr><td>
<h2>Accessibility Settings</h2>
<p>Birdsongs Frequency shifting configuration:<br>
this can be useful for hearing impaired people. Note: audio files will only be pitch shifted when the "FREQ SHIFT" button is manually clicked for a detection on the "Recordings" page.<br>
This can be useful for hearing impaired people. Note: audio files will only be pitch shifted when the "FREQ SHIFT" button is manually clicked for a detection on the "Recordings" page.<br>
<p style="margin-left: 40px">
<label for="freqshift_tool">shifting tool: </label>
<label for="freqshift_tool">Shifting tool: </label>
<select name="freqshift_tool">
<option selected="<?php print($newconfig['FREQSHIFT_TOOL']);?>"><?php print($newconfig['FREQSHIFT_TOOL']);?></option>
<?php
@@ -463,25 +484,25 @@ foreach($formats as $format){
?>
</select>
Choose here the shifting tool.<br>
Choose the shifting tool here.<br>
</p>
<p style="margin-left: 40px">
using ffmpeg:
Using ffmpeg:
e.g. origin=6000, target=4000, performs a shift of 2000 Hz down.<br>
<label for="freqshift_hi">origin [Hz]: </label>
<label for="freqshift_hi">Origin [Hz]: </label>
<input name="freqshift_hi" type="number" min="0" max="20000" step="1" value="<?php print($newconfig['FREQSHIFT_HI']);?>" required/><br>
<label for="freqshift_lo">target [Hz]: </label>
<label for="freqshift_lo">Target [Hz]: </label>
<input name="freqshift_lo" type="number" min="0" max="20000" step="1" value="<?php print($newconfig['FREQSHIFT_LO']);?>" required/>
</p>
<p style="margin-left: 40px">
using sox:
Using sox:
e.g. shiftPitch=-1200 performs a shift of 1 octave down. This value is in 100ths of a semitone.<br>
<label for="freqshift_pitch">pitch shift: </label>
<label for="freqshift_pitch">Pitch shift: </label>
<input name="freqshift_pitch" type="number" min="-4000" max="4000" step="1" value="<?php print($newconfig['FREQSHIFT_PITCH']);?>" required/><br>
</p>
</td></tr></table><br>
</p>
<br><br>
<input type="hidden" name="view" value="Advanced">
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
#List audio devices and find input device name
input_device=$(LANG=C pacmd list-sources | grep name: | grep input | awk -F'<|>' '{print $2}')
#Add echo-cancel module code to the end of the file
echo ".ifexists module-echo-cancel.so
load-module module-echo-cancel source_master=$input_device aec_method=webrtc source_name=echocancel sink_name=echocancel1
set-default-source echocancel
set-default-sink echocancel1
.endif" | sudo tee -a /etc/pulse/default.pa >/dev/null
#Reload pulseaudio
pulseaudio -k
echo "Echo cancellation setup is complete."
+997
View File
@@ -0,0 +1,997 @@
#!/usr/bin/env -S sudo -E sh
# Altered from raspi-config tool below
# https://github.com/RPi-Distro/birdnet-pi-config
#
# See LICENSE file for copyright and license details
# Copyright (c) 2012 Alex Bradbury <asb@asbradbury.org>
set -x
birdnetpi_dir=/home/pi/BirdNET-Pi
birders_config=${birdnetpi_dir}/Birders_Guide_Installer_Configuration.txt
branch=forms
INTERACTIVE=True
ASK_TO_REBOOT=0
BLACKLIST=/etc/modprobe.d/raspi-blacklist.conf
CONFIG=/boot/config.txt
USER=${SUDO_USER:-$(who -m | awk '{ print $1 }')}
is_pi () {
ARCH=$(dpkg --print-architecture)
if [ "$ARCH" = "armhf" ] || [ "$ARCH" = "arm64" ] ; then
return 0
else
return 1
fi
}
if is_pi ; then
CMDLINE=/boot/cmdline.txt
else
CMDLINE=/proc/cmdline
fi
is_pione() {
if grep -q "^Revision\s*:\s*00[0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo; then
return 0
elif grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]0[0-36][0-9a-fA-F]$" /proc/cpuinfo ; then
return 0
else
return 1
fi
}
is_pitwo() {
grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]04[0-9a-fA-F]$" /proc/cpuinfo
return $?
}
is_pizero() {
grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]0[9cC][0-9a-fA-F]$" /proc/cpuinfo
return $?
}
is_pifour() {
grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F]3[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo
return $?
}
get_pi_type() {
if is_pione; then
echo 1
elif is_pitwo; then
echo 2
else
echo 0
fi
}
is_live() {
grep -q "boot=live" $CMDLINE
return $?
}
is_ssh() {
if pstree -p | egrep --quiet --extended-regexp ".*sshd.*\($$\)"; then
return 0
else
return 1
fi
}
is_fkms() {
if grep -s -q okay /proc/device-tree/soc/v3d@7ec00000/status \
/proc/device-tree/soc/firmwarekms@7e600000/status \
/proc/device-tree/v3dbus/v3d@7ec04000/status; then
return 0
else
return 1
fi
}
is_pulseaudio() {
PS=$(ps ax)
echo "$PS" | grep -q pulseaudio
return $?
}
has_analog() {
if [ $(get_leds) -eq -1 ] ; then
return 0
else
return 1
fi
}
is_installed() {
if [ "$(dpkg -l "$1" 2> /dev/null | tail -n 1 | cut -d ' ' -f 1)" != "ii" ]; then
return 1
else
return 0
fi
}
deb_ver () {
ver=`cat /etc/debian_version | cut -d . -f 1`
echo $ver
}
calc_wt_size() {
# NOTE: it's tempting to redirect stderr to /dev/null, so supress error
# output from tput. However in this case, tput detects neither stdout or
# stderr is a tty and so only gives default 80, 24 values
WT_HEIGHT=18
WT_WIDTH=$(tput cols)
if [ -z "$WT_WIDTH" ] || [ "$WT_WIDTH" -lt 60 ]; then
WT_WIDTH=80
fi
if [ "$WT_WIDTH" -gt 178 ]; then
WT_WIDTH=120
fi
WT_MENU_HEIGHT=$(($WT_HEIGHT-7))
}
do_change_pass() {
whiptail --msgbox "You will now be asked to enter a new password for the $USER user" 20 60 1
passwd $USER &&
whiptail --msgbox "Password changed successfully" 20 60 1
}
do_configure_keyboard() {
printf "Reloading keymap. This may take a short while\n"
if [ "$INTERACTIVE" = True ]; then
dpkg-reconfigure keyboard-configuration
else
local KEYMAP="$1"
sed -i /etc/default/keyboard -e "s/^XKBLAYOUT.*/XKBLAYOUT=\"$KEYMAP\"/"
dpkg-reconfigure -f noninteractive keyboard-configuration
fi
invoke-rc.d keyboard-setup start
setsid sh -c 'exec setupcon -k --force <> /dev/tty1 >&0 2>&1'
udevadm trigger --subsystem-match=input --action=change
return 0
}
do_change_locale() {
dpkg-reconfigure locales && \
ASK_TO_REBOOT=2 && return 0
}
do_change_timezone() {
dpkg-reconfigure tzdata && \
return 0
}
get_wifi_country() {
CODE=${1:-0}
IFACE="$(list_wlan_interfaces | head -n 1)"
if [ -z "$IFACE" ]; then
whiptail --msgbox "No wireless interface found" 20 60
return 1
fi
if ! wpa_cli -i "$IFACE" status > /dev/null 2>&1; then
whiptail --msgbox "Could not communicate with wpa_supplicant" 20 60
return 1
fi
wpa_cli -i "$IFACE" save_config > /dev/null 2>&1
COUNTRY="$(wpa_cli -i "$IFACE" get country)"
if [ "$COUNTRY" = "FAIL" ]; then
return 1
fi
if [ $CODE = 0 ]; then
echo "$COUNTRY"
fi
return 0
}
do_wifi_country() {
IFACE="$(list_wlan_interfaces | head -n 1)"
if [ -z "$IFACE" ]; then
whiptail --msgbox "No wireless interface found" 20 60
return 1
fi
if ! wpa_cli -i "$IFACE" status > /dev/null 2>&1; then
whiptail --msgbox "Could not communicate with wpa_supplicant" 20 60
return 1
fi
oIFS="$IFS"
value=$(cat /usr/share/zoneinfo/iso3166.tab | tail -n +26 | tr '\t' '/' | tr '\n' '/')
IFS="/"
COUNTRY=$(whiptail --menu "Select the country in which the Pi is to be used" 20 60 10 ${value} 3>&1 1>&2 2>&3)
if [ $? -eq 0 ];then
wpa_cli -i "$IFACE" set country "$COUNTRY"
wpa_cli -i "$IFACE" save_config > /dev/null 2>&1
if iw reg set "$COUNTRY" 2> /dev/null; then
ASK_TO_REBOOT=1
fi
if hash rfkill 2> /dev/null; then
rfkill unblock wifi
if is_pi ; then
for filename in /var/lib/systemd/rfkill/*:wlan ; do
echo 0 > $filename
done
fi
fi
whiptail --msgbox "Wireless LAN country set to $COUNTRY" 20 60 1
fi
IFS=$oIFS
}
get_labels_lang() {
oIFS="$IFS"
value=$(cat ${birdnetpi_dir}/model/labels_lang.txt | tr ',' ' ' | tr '\n' ' ')
IFS=" "
get_lang=$(whiptail --menu "Select the file that corresponds to your language" 20 60 10 ${value} 3>&1 1>&2 2>&3)
labels_lang=$(awk -F, "/$get_lang/{print \$2}" ${birdnetpi_dir}/model/labels_lang.txt)
if [ $? -eq 0 ];then
whiptail --msgbox "Your installation will now use $labels_lang" 20 60 1
mv ${birdnetpi_dir}/model/labels.txt ${birdnetpi_dir}/model/labels.txt.old
unzip ${birdnetpi_dir}/model/labels_l18n.zip ${labels_lang} -d ${birdnetpi_dir}/model
mv ${birdnetpi_dir}/model/${labels_lang} ${birdnetpi_dir}/model/labels.txt
fi
IFS=$oIFS
}
get_ssh() {
if service ssh status | grep -q inactive; then
echo 1
else
echo 0
fi
}
do_ssh() {
if [ -e /var/log/regen_ssh_keys.log ] && ! grep -q "^finished" /var/log/regen_ssh_keys.log; then
whiptail --msgbox "Initial ssh key generation still running. Please wait and try again." 20 60 2
return 1
fi
DEFAULT=--defaultno
if [ $(get_ssh) -eq 0 ]; then
DEFAULT=
fi
whiptail --yesno \
"Would you like the SSH server to be enabled?\n\nCaution: Default and weak passwords are a security risk when SSH is enabled!" $DEFAULT 20 60 2
RET=$?
if [ $RET -eq 0 ]; then
ssh-keygen -A &&
update-rc.d ssh enable &&
invoke-rc.d ssh start &&
STATUS=enabled
elif [ $RET -eq 1 ]; then
update-rc.d ssh disable &&
invoke-rc.d ssh stop &&
STATUS=disabled
else
return $RET
fi
whiptail --msgbox "The SSH server is $STATUS" 20 60 1
}
get_vnc() {
if systemctl status vncserver-x11-serviced.service | grep -q -w active; then
echo 0
else
echo 1
fi
}
do_config_zram() {
if [ -e /etc/udev/rules.d/99-zram.rules ]; then
## get current swap allocation from /etc/udev/rules.d/99-zram.rules
size="$(awk -F\" '{print $4}' /etc/udev/rules.d/99-zram.rules)"
else
size="4G"
fi
new_size=$(whiptail --inputbox "How much memory (G) should the zRAM swap partition have? Choose 1G, 2G, or 4G - 4G recommended" \
19 70 -- "$size" 3>&1 1>&2 2>&3)
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_install_zram() {
echo "Configuring zram.service"
touch /etc/modules-load.d/zram.conf
echo 'zram' > /etc/modules-load.d/zram.conf
touch /etc/modprobe.d/zram.conf
echo 'options zram num_devices=1' > /etc/modprobe.d/zram.conf
touch /etc/udev/rules.d/99-zram.rules
echo "KERNEL==\"zram0\", ATTR{disksize}=\"${new_size}\",TAG+=\"systemd\"" \
> /etc/udev/rules.d/99-zram.rules
touch /etc/systemd/system/zram.service
echo "Installing zram.service"
cat << EOF > /etc/systemd/system/zram.service
[Unit]
Description=Swap with zram
After=multi-user.target
[Service]
Type=oneshot
RemainAfterExit=true
ExecStartPre=/sbin/mkswap /dev/zram0
ExecStart=/sbin/swapon /dev/zram0
ExecStop=/sbin/swapoff /dev/zram0
[Install]
WantedBy=multi-user.target
EOF
systemctl enable zram
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_zram_menu() {
whiptail --yesno "Would like to enable the zram swapping kernel module?" --defaultno 20 60 2
RET=$?
if [ $RET -eq 0 ]; then
do_config_zram
do_install_zram
STATUS=enabled
elif [ $RET -eq 1 ]; then
if [ -e /etc/udev/rules.d/99-zram.rules ];then
systemctl disable --now zram
fi
STATUS=disabled
fi
whiptail --msgbox "ZRAM swapping is $STATUS" 20 60 1
return $RET
}
do_vnc() {
DEFAULT=--defaultno
if [ $(get_vnc) -eq 0 ]; then
DEFAULT=
fi
whiptail --yesno "Would you like the VNC Server to be enabled?" $DEFAULT 20 60 2
RET=$?
if [ $RET -eq 0 ]; then
if is_installed realvnc-vnc-server || apt-get install realvnc-vnc-server; then
systemctl enable vncserver-x11-serviced.service &&
systemctl start vncserver-x11-serviced.service &&
STATUS=enabled
else
return 1
fi
elif [ $RET -eq 1 ]; then
if is_installed realvnc-vnc-server; then
systemctl disable vncserver-x11-serviced.service
systemctl stop vncserver-x11-serviced.service
fi
STATUS=disabled
else
return $RET
fi
whiptail --msgbox "The VNC Server is $STATUS" 20 60 1
}
do_audio() {
if is_pulseaudio ; then
oIFS="$IFS"
list=$(sudo -u $SUDO_USER XDG_RUNTIME_DIR=/run/user/$SUDO_UID pacmd list-sources | grep -B1 -e 'alsa.card_name' -e 'name.*input' | grep -A4 index | sed -e s/*//g | sed s/^[' '\\t]*//g | grep -e index -e card_name | sed s/'index: '//g | sed s/'alsa.card_name = '//g | sed s/\"//g | tr '\n' '\/')
if ! [ -z "$list" ] ; then
IFS="/"
AUDIO_IN=$(whiptail --menu "Choose the audio input" 20 60 10 ${list} 3>&1 1>&2 2>&3)
return 0
else
whiptail --msgbox "No internal audio devices found" 20 60 1
return 1
fi
if [ $? -eq 0 ]; then
sudo -u $SUDO_USER XDG_RUNTIME_DIR=/run/user/$SUDO_UID pactl set-default-source "$AUDIO_IN"
fi
IFS=$oIFS
fi
sudo -u $SUDO_USER pulseaudio -k
}
list_wlan_interfaces() {
for dir in /sys/class/net/*/wireless; do
if [ -d "$dir" ]; then
basename "$(dirname "$dir")"
fi
done
}
do_wifi_ssid_passphrase() {
RET=0
IFACE_LIST="$(list_wlan_interfaces)"
IFACE="$(echo "$IFACE_LIST" | head -n 1)"
if [ -z "$IFACE" ]; then
whiptail --msgbox "No wireless interface found" 20 60
return 1
fi
if ! wpa_cli -i "$IFACE" status > /dev/null 2>&1; then
whiptail --msgbox "Could not communicate with wpa_supplicant" 20 60
return 1
fi
if [ -z "$(get_wifi_country)" ]; then
do_wifi_country
fi
SSID="$1"
while [ -z "$SSID" ] ; do
SSID=$(whiptail --inputbox "Please enter SSID" 20 60 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
return 0
elif [ -z "$SSID" ]; then
whiptail --msgbox "SSID cannot be empty. Please try again." 20 60
fi
done
PASSPHRASE="$2"
PASSPHRASE=$(whiptail --passwordbox "Please enter passphrase. Leave it empty if none." 20 60 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
return 0
else
return 1
fi
# Escape special characters for embedding in regex below
local ssid="$(echo "$SSID" \
| sed 's;\\;\\\\;g' \
| sed -e 's;\.;\\\.;g' \
-e 's;\*;\\\*;g' \
-e 's;\+;\\\+;g' \
-e 's;\?;\\\?;g' \
-e 's;\^;\\\^;g' \
-e 's;\$;\\\$;g' \
-e 's;\/;\\\/;g' \
-e 's;\[;\\\[;g' \
-e 's;\];\\\];g' \
-e 's;{;\\{;g' \
-e 's;};\\};g' \
-e 's;(;\\(;g' \
-e 's;);\\);g' \
-e 's;";\\\\\";g')"
wpa_cli -i "$IFACE" list_networks \
| tail -n +2 | cut -f -2 | grep -P "\t$ssid$" | cut -f1 \
| while read ID; do
wpa_cli -i "$IFACE" remove_network "$ID" > /dev/null 2>&1
done
ID="$(wpa_cli -i "$IFACE" add_network)"
wpa_cli -i "$IFACE" set_network "$ID" ssid "\"$SSID\"" 2>&1 | grep -q "OK"
RET=$((RET + $?))
if [ -z "$PASSPHRASE" ]; then
wpa_cli -i "$IFACE" set_network "$ID" key_mgmt NONE 2>&1 | grep -q "OK"
RET=$((RET + $?))
else
wpa_cli -i "$IFACE" set_network "$ID" psk "\"$PASSPHRASE\"" 2>&1 | grep -q "OK"
RET=$((RET + $?))
fi
if [ $RET -eq 0 ]; then
wpa_cli -i "$IFACE" enable_network "$ID" > /dev/null 2>&1
else
wpa_cli -i "$IFACE" remove_network "$ID" > /dev/null 2>&1
whiptail --msgbox "Failed to set SSID or passphrase" 20 60
fi
wpa_cli -i "$IFACE" save_config > /dev/null 2>&1
echo "$IFACE_LIST" | while read IFACE; do
wpa_cli -i "$IFACE" reconfigure > /dev/null 2>&1
done
return $RET
}
do_reget_birdnetpi_urls() {
birdnetpi_url=$(whiptail --inputbox "Put your domain here. Example: \"https://birdnetpi.pmcgui.xyz\"." \
19 70 -- "https://" 3>&1 1>&2 2>&3) extractionlog_url=$(whiptail --inputbox "Put your domain here. Example: \"https://extractionlog.pmcgui.xyz\"." 19 70 -- "https://" 3>&1 1>&2 2>&3) birdnetlog_url=$(whiptail --inputbox "Put your domain here. Example: \"https://birdnetlog.pmcgui.xyz\"." 19 70 -- "https://" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
else
return 1
fi
}
do_reget_db_pwd() {
db_pwd=$(whiptail --inputbox "Please set the password the 'birder' user will use to access the database." \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_reget_birdweather_id() {
birdweather_id=$(whiptail --inputbox "Input your BirdWeather ID" \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_reget_caddy_pwd() {
caddy_pwd=$(whiptail --inputbox "Please set the password for the web interface." \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_reget_lon() {
lon="$(curl -s4 ifconfig.co/json | awk '/lon/ {print $2}' | tr -d ',')"
new_lon=$(whiptail --inputbox "Please set the longitude where recordings will take place." \
19 70 -- "$lon" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_reget_lat() {
lat="$(curl -s4 ifconfig.co/json | awk '/lat/ {print $2}' | tr -d ',')"
new_lat=$(whiptail --inputbox "Please set the latitude where recordings will take place." \
19 70 -- "$lat" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_confidence() {
confidence="$(awk -F= '/CONFIDENCE=/ {print $2}' ${birdnetpi_dir}/birdnet.conf)"
new_confidence=$(whiptail --inputbox "Please set the minimum confidence score BirdNET-Lite should use." \
19 70 -- "$confidence" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_overlap() {
overlap="$(awk -F= '/OVERLAP=/ {print $2}' ${birdnetpi_dir}/birdnet.conf)"
new_overlap=$(whiptail --inputbox "Please set the overlap BirdNET-Lite should use." \
19 70 -- "$overlap" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_sensitivity() {
sensitivity="$(awk -F= '/SENSITIVITY=/ {print $2}' ${birdnetpi_dir}/birdnet.conf)"
new_sensitivity=$(whiptail --inputbox "Please set the simoid sensitivty BirdNET-Lite should use." \
19 70 -- "$sensitivity" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_birdnetlog_url() {
birdnetlog_url=$(whiptail --inputbox "Put your domain here. Example: \"https://birdnetlog.pmcgui.xyz\"." \
19 70 -- "https://" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_extractionlog_url() {
extractionlog_url=$(whiptail --inputbox "Put your domain here. Example: \"https://extractionlog.pmcgui.xyz\"." \
19 70 -- "https://" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_birdnetpi_url() {
birdnetpi_url=$(whiptail --inputbox "Put your domain here. Example: \"https://birdnetpi.pmcgui.xyz\"." \
19 70 -- "https://" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_birdweather_id() {
birdweather_id=$(whiptail --inputbox "Input your BirdWeather ID" \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_db_pwd() {
db_pwd=$(whiptail --inputbox "Please set the password the 'birder' user will use to access the database." \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_ice_pwd() {
ice_pwd=$(whiptail --inputbox "Please set the password for the live stream." \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_caddy_pwd() {
caddy_pwd=$(whiptail --inputbox "Please set the password for the web interface." \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_lon() {
lon="$(curl -s4 ifconfig.co/json | awk '/lon/ {print $2}' | tr -d ',')"
new_lon=$(whiptail --inputbox "Please set the longitude where recordings will take place." \
19 70 -- "$lon" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_lat() {
lat="$(curl -s4 ifconfig.co/json | awk '/lat/ {print $2}' | tr -d ',')"
new_lat=$(whiptail --inputbox "Please set the latitude where recordings will take place." \
19 70 -- "$lat" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_update_os() {
apt update && apt -y full-upgrade && \
ASK_TO_REBOOT=1
}
do_update_birdnet() {
sudo -E -upi ${birdnetpi_dir}/scripts/update_birdnet.sh
}
do_install_birdnet() {
sudo -E -upi ${birdnetpi_dir}/Birders_Guide_Installer.sh && \
ASK_TO_REBOOT=1
}
insist_on_reboot() {
if [ $ASK_TO_REBOOT -eq 2 ]; then
whiptail --yesno "You really should reboot. Do that now?" 20 60 2
if [ $? -eq 0 ]; then # yes
sync
reboot
fi
fi
}
do_finish() {
if [ $ASK_TO_REBOOT -eq 1 ]; then
whiptail --yesno "Would you like to reboot now?" 20 60 2
if [ $? -eq 0 ]; then # yes
sync
reboot
fi
elif [ $ASK_TO_REBOOT -eq 2 ]; then
whiptail --yesno "You really should reboot. Do that now?" 20 60 2
if [ $? -eq 0 ]; then # yes
sync
reboot
fi
fi
exit 0
}
# Everything else needs to be run as root
if [ $(id -u) -ne 0 ]; then
printf "Script must be run as root. Try 'sudo birdnet-pi-config'\n"
exit 1
fi
do_advanced_reconfig_menu() {
MENU=9
while [ $MENU -eq 9 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "BirdNET-Pi Configuration Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"Sensitivity" "Set the sigmoid sensitivity" \
"Confidence" "Set the minimum confidence score required for a detection" \
"Overlap" "Set the analysis overlap in seconds" \
"BirdNET-Pi URLs" "Set the URLs your installation should use" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
Sens*) do_get_sensitivity;;
Confi*) do_get_confidence;;
Overlap*) do_get_overlap;;
Bird*) do_reget_birdnetpi_urls;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_interface_menu() {
MENU=6
while [ $MENU -eq 6 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "Interfacing Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"SSH" "Enable/disable remote command line access using SSH" \
"VNC" "Enable/disable graphical remote access using RealVNC" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
SSH*) do_ssh ;;
VNC*) do_vnc ;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_advanced_config_menu() {
MENU=5
while [ $MENU -eq 5 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "BirdNET-Pi Configuration Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"Sensitivity" "Set the sigmoid sensitivity" \
"Confidence" "Set the minimum confidence score required for a detection" \
"Overlap" "Set the analysis overlap in seconds" \
"BirdNET-Pi URLs" "Set the URLs your installation should use" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
Sens*) do_get_sensitivity;;
Confi*) do_get_confidence;;
Overlap*) do_get_overlap;;
Bird*) do_get_birdnetpi_url; do_get_birdnetlog_url; do_get_extractionlog_url;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_system_menu() {
MENU=4
while [ $MENU -eq 4 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "System Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"Password" "Change password for the '$USER' user" \
"Audio" "Select audio device you'd like BirdNET-Pi to use" \
"Wireless LAN" "Enter SSID and passphrase" \
"Update the OS" "Update the underlying operating system" \
"Interface Options" "Enable/Disable SSH and VNC" \
"Configure zRAM" "Enable/Disable zRAM" \
"Timezone" "Configure time zone" \
"Keyboard" "Set keyboard layout to match your keyboard" \
"WLAN Country" "Set legal wireless channels for your country" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
Password*) do_change_pass ;;
Audio*) do_audio ;;
Wireless*) do_wifi_ssid_passphrase ;;
Update*) do_update_os ;;
Interface*) do_interface_menu ;;
Configure*) do_zram_menu ;;
Time*) do_change_timezone; insist_on_reboot ;;
Key*) do_configure_keyboard ;;
WLAN*) do_wifi_country ;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_reconfig_birdnet_menu() {
MENU=3
while [ $MENU -eq 3 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "BirdNET-Pi Re-configuration Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"Latitude " "Set the latitude your system should use" \
"Longitude" "Set the longitude your system should use" \
"Caddy Password" "Set the web interface password" \
"Database Password" "Set the password BirdNET-Pi will use to access the DB" \
"BirdWeather ID" "Input your BirdWeather ID if you have one" \
"Advanced Settings" "Sensitivity, Overlap, Confidence, URLs" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
Lat*) do_reget_lat;;
Lon*) do_reget_lon;;
Cad*) do_reget_caddy_pwd;;
Data*) do_reget_db_pwd;;
Bird*) do_reget_birdweather_id;;
Advanced*) do_advanced_reconfig_menu;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_config_birdnet_menu() {
MENU=2
while [ $MENU -eq 2 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "BirdNET-Pi Configuration Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"Latitude " "Set the latitude your system should use" \
"Longitude" "Set the longitude your system should use" \
"Caddy Password" "Set the web interface password" \
"IceCast2 Password" "Set the IceCast2 password" \
"Database Password" "Set the password BirdNET-Pi will use to access the DB" \
"BirdWeather ID" "Input your BirdWeather ID if you have one" \
"Set Custom URLS" "Designate custom URLs for the web services if you've set that up" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
Lat*) do_get_lat;;
Lon*) do_get_lon;;
Cad*) do_get_caddy_pwd;;
Ice*) do_get_ice_pwd;;
Data*) do_get_db_pwd;;
Bird*) do_get_birdweather_id;;
Set*) do_get_birdnetpi_url; do_get_birdnetlog_url; do_get_extractionlog_url;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_internationalisation_menu() {
MENU=1
while [ $MENU -eq 1 ];do
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "Localisation Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"System Locale" "Configure the system language and regional settings" \
"labels.txt" "Set the language to use for species detection" \
"Time Zone" "Set the Time Zone" \
3>&1 1>&2 2>&3)
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
System*) do_change_locale;;
label*) get_labels_lang;;
Time*) do_change_timezone;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
#
# Interactive use loop
#
[ -e $CONFIG ] || touch $CONFIG
calc_wt_size
while [ "$USER" = "root" ] || [ -z "$USER" ]; do
if ! USER=$(whiptail --inputbox "birdnet-pi-config could not determine the default user.\\n\\nWhat user should these settings apply to?" 20 60 pi 3>&1 1>&2 2>&3); then
return 0
fi
done
while true; do
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --backtitle "$(cat /proc/device-tree/model)" --menu "Setup Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Finish --ok-button Select \
"Step 1" "Configure language and timezone" \
"Step 2" "Configure BirdNET-Pi before installation" \
"Step 3" "Install BirdNET-Pi" \
"Step 4" "Reconfigure BirdNET-Pi AFTER installation" \
"Step 5" "Update BirdNET-Pi (run this if you changed URLs)" \
"System Options" "Configure system settings" \
3>&1 1>&2 2>&3)
RET=$?
if [ $RET -eq 1 ]; then
do_finish
elif [ $RET -eq 0 ]; then
case "$FUN" in
*1) do_internationalisation_menu ;;
*2) do_config_birdnet_menu ;;
*3) do_install_birdnet;;
*4) do_reconfig_birdnet_menu ;;
*5) do_update_birdnet;;
Sys*) do_system_menu ;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
+997
View File
@@ -0,0 +1,997 @@
#!/usr/bin/env -S sudo -E sh
# Altered from raspi-config tool below
# https://github.com/RPi-Distro/birdnet-pi-config
#
# See LICENSE file for copyright and license details
# Copyright (c) 2012 Alex Bradbury <asb@asbradbury.org>
set -x
birdnetpi_dir=/home/pi/BirdNET-Pi
birders_config=${birdnetpi_dir}/Birders_Guide_Installer_Configuration.txt
branch=forms
INTERACTIVE=True
ASK_TO_REBOOT=0
BLACKLIST=/etc/modprobe.d/raspi-blacklist.conf
CONFIG=/boot/config.txt
USER=${SUDO_USER:-$(who -m | awk '{ print $1 }')}
is_pi () {
ARCH=$(dpkg --print-architecture)
if [ "$ARCH" = "armhf" ] || [ "$ARCH" = "arm64" ] ; then
return 0
else
return 1
fi
}
if is_pi ; then
CMDLINE=/boot/cmdline.txt
else
CMDLINE=/proc/cmdline
fi
is_pione() {
if grep -q "^Revision\s*:\s*00[0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo; then
return 0
elif grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]0[0-36][0-9a-fA-F]$" /proc/cpuinfo ; then
return 0
else
return 1
fi
}
is_pitwo() {
grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]04[0-9a-fA-F]$" /proc/cpuinfo
return $?
}
is_pizero() {
grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]0[9cC][0-9a-fA-F]$" /proc/cpuinfo
return $?
}
is_pifour() {
grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F]3[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$" /proc/cpuinfo
return $?
}
get_pi_type() {
if is_pione; then
echo 1
elif is_pitwo; then
echo 2
else
echo 0
fi
}
is_live() {
grep -q "boot=live" $CMDLINE
return $?
}
is_ssh() {
if pstree -p | egrep --quiet --extended-regexp ".*sshd.*\($$\)"; then
return 0
else
return 1
fi
}
is_fkms() {
if grep -s -q okay /proc/device-tree/soc/v3d@7ec00000/status \
/proc/device-tree/soc/firmwarekms@7e600000/status \
/proc/device-tree/v3dbus/v3d@7ec04000/status; then
return 0
else
return 1
fi
}
is_pulseaudio() {
PS=$(ps ax)
echo "$PS" | grep -q pulseaudio
return $?
}
has_analog() {
if [ $(get_leds) -eq -1 ] ; then
return 0
else
return 1
fi
}
is_installed() {
if [ "$(dpkg -l "$1" 2> /dev/null | tail -n 1 | cut -d ' ' -f 1)" != "ii" ]; then
return 1
else
return 0
fi
}
deb_ver () {
ver=`cat /etc/debian_version | cut -d . -f 1`
echo $ver
}
calc_wt_size() {
# NOTE: it's tempting to redirect stderr to /dev/null, so supress error
# output from tput. However in this case, tput detects neither stdout or
# stderr is a tty and so only gives default 80, 24 values
WT_HEIGHT=18
WT_WIDTH=$(tput cols)
if [ -z "$WT_WIDTH" ] || [ "$WT_WIDTH" -lt 60 ]; then
WT_WIDTH=80
fi
if [ "$WT_WIDTH" -gt 178 ]; then
WT_WIDTH=120
fi
WT_MENU_HEIGHT=$(($WT_HEIGHT-7))
}
do_change_pass() {
whiptail --msgbox "You will now be asked to enter a new password for the $USER user" 20 60 1
passwd $USER &&
whiptail --msgbox "Password changed successfully" 20 60 1
}
do_configure_keyboard() {
printf "Reloading keymap. This may take a short while\n"
if [ "$INTERACTIVE" = True ]; then
dpkg-reconfigure keyboard-configuration
else
local KEYMAP="$1"
sed -i /etc/default/keyboard -e "s/^XKBLAYOUT.*/XKBLAYOUT=\"$KEYMAP\"/"
dpkg-reconfigure -f noninteractive keyboard-configuration
fi
invoke-rc.d keyboard-setup start
setsid sh -c 'exec setupcon -k --force <> /dev/tty1 >&0 2>&1'
udevadm trigger --subsystem-match=input --action=change
return 0
}
do_change_locale() {
dpkg-reconfigure locales && \
ASK_TO_REBOOT=2 && return 0
}
do_change_timezone() {
dpkg-reconfigure tzdata && \
return 0
}
get_wifi_country() {
CODE=${1:-0}
IFACE="$(list_wlan_interfaces | head -n 1)"
if [ -z "$IFACE" ]; then
whiptail --msgbox "No wireless interface found" 20 60
return 1
fi
if ! wpa_cli -i "$IFACE" status > /dev/null 2>&1; then
whiptail --msgbox "Could not communicate with wpa_supplicant" 20 60
return 1
fi
wpa_cli -i "$IFACE" save_config > /dev/null 2>&1
COUNTRY="$(wpa_cli -i "$IFACE" get country)"
if [ "$COUNTRY" = "FAIL" ]; then
return 1
fi
if [ $CODE = 0 ]; then
echo "$COUNTRY"
fi
return 0
}
do_wifi_country() {
IFACE="$(list_wlan_interfaces | head -n 1)"
if [ -z "$IFACE" ]; then
whiptail --msgbox "No wireless interface found" 20 60
return 1
fi
if ! wpa_cli -i "$IFACE" status > /dev/null 2>&1; then
whiptail --msgbox "Could not communicate with wpa_supplicant" 20 60
return 1
fi
oIFS="$IFS"
value=$(cat /usr/share/zoneinfo/iso3166.tab | tail -n +26 | tr '\t' '/' | tr '\n' '/')
IFS="/"
COUNTRY=$(whiptail --menu "Select the country in which the Pi is to be used" 20 60 10 ${value} 3>&1 1>&2 2>&3)
if [ $? -eq 0 ];then
wpa_cli -i "$IFACE" set country "$COUNTRY"
wpa_cli -i "$IFACE" save_config > /dev/null 2>&1
if iw reg set "$COUNTRY" 2> /dev/null; then
ASK_TO_REBOOT=1
fi
if hash rfkill 2> /dev/null; then
rfkill unblock wifi
if is_pi ; then
for filename in /var/lib/systemd/rfkill/*:wlan ; do
echo 0 > $filename
done
fi
fi
whiptail --msgbox "Wireless LAN country set to $COUNTRY" 20 60 1
fi
IFS=$oIFS
}
get_labels_lang() {
oIFS="$IFS"
value=$(cat ${birdnetpi_dir}/model/labels_lang.txt | tr ',' ' ' | tr '\n' ' ')
IFS=" "
get_lang=$(whiptail --menu "Select the file that corresponds to your language" 20 60 10 ${value} 3>&1 1>&2 2>&3)
labels_lang=$(awk -F, "/$get_lang/{print \$2}" ${birdnetpi_dir}/model/labels_lang.txt)
if [ $? -eq 0 ];then
whiptail --msgbox "Your installation will now use $labels_lang" 20 60 1
mv ${birdnetpi_dir}/model/labels.txt ${birdnetpi_dir}/model/labels.txt.old
unzip ${birdnetpi_dir}/model/labels_l18n.zip ${labels_lang} -d ${birdnetpi_dir}/model
mv ${birdnetpi_dir}/model/${labels_lang} ${birdnetpi_dir}/model/labels.txt
fi
IFS=$oIFS
}
get_ssh() {
if service ssh status | grep -q inactive; then
echo 1
else
echo 0
fi
}
do_ssh() {
if [ -e /var/log/regen_ssh_keys.log ] && ! grep -q "^finished" /var/log/regen_ssh_keys.log; then
whiptail --msgbox "Initial ssh key generation still running. Please wait and try again." 20 60 2
return 1
fi
DEFAULT=--defaultno
if [ $(get_ssh) -eq 0 ]; then
DEFAULT=
fi
whiptail --yesno \
"Would you like the SSH server to be enabled?\n\nCaution: Default and weak passwords are a security risk when SSH is enabled!" $DEFAULT 20 60 2
RET=$?
if [ $RET -eq 0 ]; then
ssh-keygen -A &&
update-rc.d ssh enable &&
invoke-rc.d ssh start &&
STATUS=enabled
elif [ $RET -eq 1 ]; then
update-rc.d ssh disable &&
invoke-rc.d ssh stop &&
STATUS=disabled
else
return $RET
fi
whiptail --msgbox "The SSH server is $STATUS" 20 60 1
}
get_vnc() {
if systemctl status vncserver-x11-serviced.service | grep -q -w active; then
echo 0
else
echo 1
fi
}
do_config_zram() {
if [ -e /etc/udev/rules.d/99-zram.rules ]; then
## get current swap allocation from /etc/udev/rules.d/99-zram.rules
size="$(awk -F\" '{print $4}' /etc/udev/rules.d/99-zram.rules)"
else
size="4G"
fi
new_size=$(whiptail --inputbox "How much memory (G) should the zRAM swap partition have? Choose 1G, 2G, or 4G - 4G recommended" \
19 70 -- "$size" 3>&1 1>&2 2>&3)
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_install_zram() {
echo "Configuring zram.service"
touch /etc/modules-load.d/zram.conf
echo 'zram' > /etc/modules-load.d/zram.conf
touch /etc/modprobe.d/zram.conf
echo 'options zram num_devices=1' > /etc/modprobe.d/zram.conf
touch /etc/udev/rules.d/99-zram.rules
echo "KERNEL==\"zram0\", ATTR{disksize}=\"${new_size}\",TAG+=\"systemd\"" \
> /etc/udev/rules.d/99-zram.rules
touch /etc/systemd/system/zram.service
echo "Installing zram.service"
cat << EOF > /etc/systemd/system/zram.service
[Unit]
Description=Swap with zram
After=multi-user.target
[Service]
Type=oneshot
RemainAfterExit=true
ExecStartPre=/sbin/mkswap /dev/zram0
ExecStart=/sbin/swapon /dev/zram0
ExecStop=/sbin/swapoff /dev/zram0
[Install]
WantedBy=multi-user.target
EOF
systemctl enable zram
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_zram_menu() {
whiptail --yesno "Would like to enable the zram swapping kernel module?" --defaultno 20 60 2
RET=$?
if [ $RET -eq 0 ]; then
do_config_zram
do_install_zram
STATUS=enabled
elif [ $RET -eq 1 ]; then
if [ -e /etc/udev/rules.d/99-zram.rules ];then
systemctl disable --now zram
fi
STATUS=disabled
fi
whiptail --msgbox "ZRAM swapping is $STATUS" 20 60 1
return $RET
}
do_vnc() {
DEFAULT=--defaultno
if [ $(get_vnc) -eq 0 ]; then
DEFAULT=
fi
whiptail --yesno "Would you like the VNC Server to be enabled?" $DEFAULT 20 60 2
RET=$?
if [ $RET -eq 0 ]; then
if is_installed realvnc-vnc-server || apt-get install realvnc-vnc-server; then
systemctl enable vncserver-x11-serviced.service &&
systemctl start vncserver-x11-serviced.service &&
STATUS=enabled
else
return 1
fi
elif [ $RET -eq 1 ]; then
if is_installed realvnc-vnc-server; then
systemctl disable vncserver-x11-serviced.service
systemctl stop vncserver-x11-serviced.service
fi
STATUS=disabled
else
return $RET
fi
whiptail --msgbox "The VNC Server is $STATUS" 20 60 1
}
do_audio() {
if is_pulseaudio ; then
oIFS="$IFS"
list=$(sudo -u $SUDO_USER XDG_RUNTIME_DIR=/run/user/$SUDO_UID pacmd list-sources | grep -B1 -e 'alsa.card_name' -e 'name.*input' | grep -A4 index | sed -e s/*//g | sed s/^[' '\\t]*//g | grep -e index -e card_name | sed s/'index: '//g | sed s/'alsa.card_name = '//g | sed s/\"//g | tr '\n' '\/')
if ! [ -z "$list" ] ; then
IFS="/"
AUDIO_IN=$(whiptail --menu "Choose the audio input" 20 60 10 ${list} 3>&1 1>&2 2>&3)
return 0
else
whiptail --msgbox "No internal audio devices found" 20 60 1
return 1
fi
if [ $? -eq 0 ]; then
sudo -u $SUDO_USER XDG_RUNTIME_DIR=/run/user/$SUDO_UID pactl set-default-source "$AUDIO_IN"
fi
IFS=$oIFS
fi
sudo -u $SUDO_USER pulseaudio -k
}
list_wlan_interfaces() {
for dir in /sys/class/net/*/wireless; do
if [ -d "$dir" ]; then
basename "$(dirname "$dir")"
fi
done
}
do_wifi_ssid_passphrase() {
RET=0
IFACE_LIST="$(list_wlan_interfaces)"
IFACE="$(echo "$IFACE_LIST" | head -n 1)"
if [ -z "$IFACE" ]; then
whiptail --msgbox "No wireless interface found" 20 60
return 1
fi
if ! wpa_cli -i "$IFACE" status > /dev/null 2>&1; then
whiptail --msgbox "Could not communicate with wpa_supplicant" 20 60
return 1
fi
if [ -z "$(get_wifi_country)" ]; then
do_wifi_country
fi
SSID="$1"
while [ -z "$SSID" ] ; do
SSID=$(whiptail --inputbox "Please enter SSID" 20 60 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
return 0
elif [ -z "$SSID" ]; then
whiptail --msgbox "SSID cannot be empty. Please try again." 20 60
fi
done
PASSPHRASE="$2"
PASSPHRASE=$(whiptail --passwordbox "Please enter passphrase. Leave it empty if none." 20 60 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
return 0
else
return 1
fi
# Escape special characters for embedding in regex below
local ssid="$(echo "$SSID" \
| sed 's;\\;\\\\;g' \
| sed -e 's;\.;\\\.;g' \
-e 's;\*;\\\*;g' \
-e 's;\+;\\\+;g' \
-e 's;\?;\\\?;g' \
-e 's;\^;\\\^;g' \
-e 's;\$;\\\$;g' \
-e 's;\/;\\\/;g' \
-e 's;\[;\\\[;g' \
-e 's;\];\\\];g' \
-e 's;{;\\{;g' \
-e 's;};\\};g' \
-e 's;(;\\(;g' \
-e 's;);\\);g' \
-e 's;";\\\\\";g')"
wpa_cli -i "$IFACE" list_networks \
| tail -n +2 | cut -f -2 | grep -P "\t$ssid$" | cut -f1 \
| while read ID; do
wpa_cli -i "$IFACE" remove_network "$ID" > /dev/null 2>&1
done
ID="$(wpa_cli -i "$IFACE" add_network)"
wpa_cli -i "$IFACE" set_network "$ID" ssid "\"$SSID\"" 2>&1 | grep -q "OK"
RET=$((RET + $?))
if [ -z "$PASSPHRASE" ]; then
wpa_cli -i "$IFACE" set_network "$ID" key_mgmt NONE 2>&1 | grep -q "OK"
RET=$((RET + $?))
else
wpa_cli -i "$IFACE" set_network "$ID" psk "\"$PASSPHRASE\"" 2>&1 | grep -q "OK"
RET=$((RET + $?))
fi
if [ $RET -eq 0 ]; then
wpa_cli -i "$IFACE" enable_network "$ID" > /dev/null 2>&1
else
wpa_cli -i "$IFACE" remove_network "$ID" > /dev/null 2>&1
whiptail --msgbox "Failed to set SSID or passphrase" 20 60
fi
wpa_cli -i "$IFACE" save_config > /dev/null 2>&1
echo "$IFACE_LIST" | while read IFACE; do
wpa_cli -i "$IFACE" reconfigure > /dev/null 2>&1
done
return $RET
}
do_reget_birdnetpi_urls() {
birdnetpi_url=$(whiptail --inputbox "Put your domain here. Example: \"https://birdnetpi.pmcgui.xyz\"." \
19 70 -- "https://" 3>&1 1>&2 2>&3) extractionlog_url=$(whiptail --inputbox "Put your domain here. Example: \"https://extractionlog.pmcgui.xyz\"." 19 70 -- "https://" 3>&1 1>&2 2>&3) birdnetlog_url=$(whiptail --inputbox "Put your domain here. Example: \"https://birdnetlog.pmcgui.xyz\"." 19 70 -- "https://" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
else
return 1
fi
}
do_reget_db_pwd() {
db_pwd=$(whiptail --inputbox "Please set the password the 'birder' user will use to access the database." \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_reget_birdweather_id() {
birdweather_id=$(whiptail --inputbox "Input your BirdWeather ID" \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_reget_caddy_pwd() {
caddy_pwd=$(whiptail --inputbox "Please set the password for the web interface." \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_reget_lon() {
lon="$(curl -s4 ifconfig.co/json | awk '/lon/ {print $2}' | tr -d ',')"
new_lon=$(whiptail --inputbox "Please set the longitude where recordings will take place." \
19 70 -- "$lon" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_reget_lat() {
lat="$(curl -s4 ifconfig.co/json | awk '/lat/ {print $2}' | tr -d ',')"
new_lat=$(whiptail --inputbox "Please set the latitude where recordings will take place." \
19 70 -- "$lat" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_confidence() {
confidence="$(awk -F= '/CONFIDENCE=/ {print $2}' ${birdnetpi_dir}/birdnet.conf)"
new_confidence=$(whiptail --inputbox "Please set the minimum confidence score BirdNET-Lite should use." \
19 70 -- "$confidence" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_overlap() {
overlap="$(awk -F= '/OVERLAP=/ {print $2}' ${birdnetpi_dir}/birdnet.conf)"
new_overlap=$(whiptail --inputbox "Please set the overlap BirdNET-Lite should use." \
19 70 -- "$overlap" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_sensitivity() {
sensitivity="$(awk -F= '/SENSITIVITY=/ {print $2}' ${birdnetpi_dir}/birdnet.conf)"
new_sensitivity=$(whiptail --inputbox "Please set the simoid sensitivty BirdNET-Lite should use." \
19 70 -- "$sensitivity" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/rewrite_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_birdnetlog_url() {
birdnetlog_url=$(whiptail --inputbox "Put your domain here. Example: \"https://birdnetlog.pmcgui.xyz\"." \
19 70 -- "https://" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_extractionlog_url() {
extractionlog_url=$(whiptail --inputbox "Put your domain here. Example: \"https://extractionlog.pmcgui.xyz\"." \
19 70 -- "https://" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_birdnetpi_url() {
birdnetpi_url=$(whiptail --inputbox "Put your domain here. Example: \"https://birdnetpi.pmcgui.xyz\"." \
19 70 -- "https://" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_birdweather_id() {
birdweather_id=$(whiptail --inputbox "Input your BirdWeather ID" \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_db_pwd() {
db_pwd=$(whiptail --inputbox "Please set the password the 'birder' user will use to access the database." \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_ice_pwd() {
ice_pwd=$(whiptail --inputbox "Please set the password for the live stream." \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_caddy_pwd() {
caddy_pwd=$(whiptail --inputbox "Please set the password for the web interface." \
19 70 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_lon() {
lon="$(curl -s4 ifconfig.co/json | awk '/lon/ {print $2}' | tr -d ',')"
new_lon=$(whiptail --inputbox "Please set the longitude where recordings will take place." \
19 70 -- "$lon" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_get_lat() {
lat="$(curl -s4 ifconfig.co/json | awk '/lat/ {print $2}' | tr -d ',')"
new_lat=$(whiptail --inputbox "Please set the latitude where recordings will take place." \
19 70 -- "$lat" 3>&1 1>&2 2>&3) ${birdnetpi_dir}/scripts/write_config.sh
if [ $? -eq 0 ];then
return 0
ASK_TO_REBOOT=2
else
return 1
fi
}
do_update_os() {
apt update && apt -y full-upgrade && \
ASK_TO_REBOOT=1
}
do_update_birdnet() {
sudo -E -upi ${birdnetpi_dir}/scripts/update_birdnet.sh
}
do_install_birdnet() {
sudo -E -upi ${birdnetpi_dir}/Birders_Guide_Installer.sh && \
ASK_TO_REBOOT=1
}
insist_on_reboot() {
if [ $ASK_TO_REBOOT -eq 2 ]; then
whiptail --yesno "You really should reboot. Do that now?" 20 60 2
if [ $? -eq 0 ]; then # yes
sync
reboot
fi
fi
}
do_finish() {
if [ $ASK_TO_REBOOT -eq 1 ]; then
whiptail --yesno "Would you like to reboot now?" 20 60 2
if [ $? -eq 0 ]; then # yes
sync
reboot
fi
elif [ $ASK_TO_REBOOT -eq 2 ]; then
whiptail --yesno "You really should reboot. Do that now?" 20 60 2
if [ $? -eq 0 ]; then # yes
sync
reboot
fi
fi
exit 0
}
# Everything else needs to be run as root
if [ $(id -u) -ne 0 ]; then
printf "Script must be run as root. Try 'sudo birdnet-pi-config'\n"
exit 1
fi
do_advanced_reconfig_menu() {
MENU=9
while [ $MENU -eq 9 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "BirdNET-Pi Configuration Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"Sensitivity" "Set the sigmoid sensitivity" \
"Confidence" "Set the minimum confidence score required for a detection" \
"Overlap" "Set the analysis overlap in seconds" \
"BirdNET-Pi URLs" "Set the URLs your installation should use" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
Sens*) do_get_sensitivity;;
Confi*) do_get_confidence;;
Overlap*) do_get_overlap;;
Bird*) do_reget_birdnetpi_urls;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_interface_menu() {
MENU=6
while [ $MENU -eq 6 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "Interfacing Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"SSH" "Enable/disable remote command line access using SSH" \
"VNC" "Enable/disable graphical remote access using RealVNC" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
SSH*) do_ssh ;;
VNC*) do_vnc ;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_advanced_config_menu() {
MENU=5
while [ $MENU -eq 5 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "BirdNET-Pi Configuration Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"Sensitivity" "Set the sigmoid sensitivity" \
"Confidence" "Set the minimum confidence score required for a detection" \
"Overlap" "Set the analysis overlap in seconds" \
"BirdNET-Pi URLs" "Set the URLs your installation should use" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
Sens*) do_get_sensitivity;;
Confi*) do_get_confidence;;
Overlap*) do_get_overlap;;
Bird*) do_get_birdnetpi_url; do_get_birdnetlog_url; do_get_extractionlog_url;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_system_menu() {
MENU=4
while [ $MENU -eq 4 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "System Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"Password" "Change password for the '$USER' user" \
"Audio" "Select audio device you'd like BirdNET-Pi to use" \
"Wireless LAN" "Enter SSID and passphrase" \
"Update the OS" "Update the underlying operating system" \
"Interface Options" "Enable/Disable SSH and VNC" \
"Configure zRAM" "Enable/Disable zRAM" \
"Timezone" "Configure time zone" \
"Keyboard" "Set keyboard layout to match your keyboard" \
"WLAN Country" "Set legal wireless channels for your country" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
Password*) do_change_pass ;;
Audio*) do_audio ;;
Wireless*) do_wifi_ssid_passphrase ;;
Update*) do_update_os ;;
Interface*) do_interface_menu ;;
Configure*) do_zram_menu ;;
Time*) do_change_timezone; insist_on_reboot ;;
Key*) do_configure_keyboard ;;
WLAN*) do_wifi_country ;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_reconfig_birdnet_menu() {
MENU=3
while [ $MENU -eq 3 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "BirdNET-Pi Re-configuration Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"Latitude " "Set the latitude your system should use" \
"Longitude" "Set the longitude your system should use" \
"Caddy Password" "Set the web interface password" \
"Database Password" "Set the password BirdNET-Pi will use to access the DB" \
"BirdWeather ID" "Input your BirdWeather ID if you have one" \
"Advanced Settings" "Sensitivity, Overlap, Confidence, URLs" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
Lat*) do_reget_lat;;
Lon*) do_reget_lon;;
Cad*) do_reget_caddy_pwd;;
Data*) do_reget_db_pwd;;
Bird*) do_reget_birdweather_id;;
Advanced*) do_advanced_reconfig_menu;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_config_birdnet_menu() {
MENU=2
while [ $MENU -eq 2 ];do
if is_pi ; then
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "BirdNET-Pi Configuration Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"Latitude " "Set the latitude your system should use" \
"Longitude" "Set the longitude your system should use" \
"Caddy Password" "Set the web interface password" \
"IceCast2 Password" "Set the IceCast2 password" \
"Database Password" "Set the password BirdNET-Pi will use to access the DB" \
"BirdWeather ID" "Input your BirdWeather ID if you have one" \
"Set Custom URLS" "Designate custom URLs for the web services if you've set that up" \
3>&1 1>&2 2>&3)
else
exit
fi
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
Lat*) do_get_lat;;
Lon*) do_get_lon;;
Cad*) do_get_caddy_pwd;;
Ice*) do_get_ice_pwd;;
Data*) do_get_db_pwd;;
Bird*) do_get_birdweather_id;;
Set*) do_get_birdnetpi_url; do_get_birdnetlog_url; do_get_extractionlog_url;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
do_internationalisation_menu() {
MENU=1
while [ $MENU -eq 1 ];do
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --menu "Localisation Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Back --ok-button Select \
"System Locale" "Configure the system language and regional settings" \
"labels.txt" "Set the language to use for species detection" \
"Time Zone" "Set the Time Zone" \
3>&1 1>&2 2>&3)
RET=$?
if [ $RET -eq 1 ]; then
return 0
MENU=0
elif [ $RET -eq 0 ]; then
case "$FUN" in
System*) do_change_locale;;
label*) get_labels_lang;;
Time*) do_change_timezone;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
}
#
# Interactive use loop
#
[ -e $CONFIG ] || touch $CONFIG
calc_wt_size
while [ "$USER" = "root" ] || [ -z "$USER" ]; do
if ! USER=$(whiptail --inputbox "birdnet-pi-config could not determine the default user.\\n\\nWhat user should these settings apply to?" 20 60 pi 3>&1 1>&2 2>&3); then
return 0
fi
done
while true; do
FUN=$(whiptail --title "BirdNET-Pi Software Configuration Tool (birdnet-pi-config)" --backtitle "$(cat /proc/device-tree/model)" --menu "Setup Options" $WT_HEIGHT $WT_WIDTH $WT_MENU_HEIGHT --cancel-button Finish --ok-button Select \
"Step 1" "Configure language and timezone" \
"Step 2" "Configure BirdNET-Pi before installation" \
"Step 3" "Install BirdNET-Pi" \
"Step 4" "Reconfigure BirdNET-Pi AFTER installation" \
"Step 5" "Update BirdNET-Pi (run this if you changed URLs)" \
"System Options" "Configure system settings" \
3>&1 1>&2 2>&3)
RET=$?
if [ $RET -eq 1 ]; then
do_finish
elif [ $RET -eq 0 ]; then
case "$FUN" in
*1) do_internationalisation_menu ;;
*2) do_config_birdnet_menu ;;
*3) do_install_birdnet;;
*4) do_reconfig_birdnet_menu ;;
*5) do_update_birdnet;;
Sys*) do_system_menu ;;
*) whiptail --msgbox "Programmer error: unrecognized option" 20 60 1 ;;
esac || whiptail --msgbox "There was an error running option $FUN" 20 60 1
fi
done
+4 -2
View File
@@ -472,9 +472,10 @@ function runProcess() {
<dl>
<dt>BirdNET_6K_GLOBAL_MODEL (2020)</dt><br>
<dd id="ddnewline">This is the BirdNET-Lite model, with bird sound recognition for more than 6,000 species worldwide. This is the default option and will generally work very for people in most of the world.</dd>
<br>
<dt>BirdNET_GLOBAL_3K_V2.2_Model_FP16 (2022)</dt><br>
<dd id="ddnewline">This is the BirdNET-Analyzer model, a newer work-in-progress project with aims to improve on the BirdNET-Lite model. Currently it only supports about 3,500 species worldwide, meaning for some regions (North America, Europe, Australia) it will usually outperform the BirdNET-Lite model, but for other regions it will be worse.</dd><br>
<dt>(in-depth technical write-up on the models <a target="_blank" href="https://github.com/mcguirepr89/BirdNET-Pi/wiki/BirdNET-Pi:-some-theory-on-classification-&-some-practical-hints">here</a>)</dt>
<dt>[ In-depth technical write-up on the models <a target="_blank" href="https://github.com/mcguirepr89/BirdNET-Pi/wiki/BirdNET-Pi:-some-theory-on-classification-&-some-practical-hints">here</a> ]</dt>
</dl>
</td></tr></table><br>
@@ -482,7 +483,7 @@ function runProcess() {
<h2>Location</h2>
<label for="site_name">Site Name: </label>
<input name="site_name" type="text" value="<?php print($config['SITE_NAME']);?>"/> (Optional)<br>
<label for="latitude">Latitude: </label>
<label for="latitude">Latitude: &nbsp; &nbsp;</label>
<input name="latitude" type="number" max="90" min="-90" step="0.0001" value="<?php print($config['LATITUDE']);?>" required/><br>
<label for="longitude">Longitude: </label>
<input name="longitude" type="number" max="180" min="-180" step="0.0001" value="<?php print($config['LONGITUDE']);?>" required/><br>
@@ -650,6 +651,7 @@ https://discordapp.com/api/webhooks/{WebhookID}/{WebhookToken}
?>
<input onclick="this.showPicker()" type="date" id="date" name="date" value="<?php echo $date->format('Y-m-d') ?>" <?php echo $disabledvalue; ?>>
<input onclick="this.showPicker()" type="time" id="time" name="time" value="<?php echo $date->format('H:i'); ?>" <?php echo $disabledvalue; ?>><br>
<br>
<label for="timezone">Select a Timezone: </label>
<select name="timezone">
<option disabled selected>
+1 -2
View File
@@ -4,8 +4,7 @@
echo "........................................IPs....................................."
echo "LAN IP: $(hostname -I|cut -d' ' -f1)"
echo "Public IP: $(curl -s4 ifconfig.co)"
if [ ! "$(dpkg -l | ! grep -q "vcgencmd")" ]
then
if ! dpkg -l | grep -q vcgencmd ; then
echo "..................................\`vcgencmd stats\`.............................."
sudo -u$USER vcgencmd get_throttled
hex=$(sudo -u$USER vcgencmd get_throttled | cut -d'x' -f2)
View File
+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash
# Set variables
db_path=~/BirdNET-Pi/scripts/birds.db
dir_path=~/BirdSongs/Extracted/By_Date
# Calculate the average count of detections for each species
avg_counts=$(sqlite3 $db_path "SELECT Com_Name || '|' || AVG(count) FROM (SELECT Com_Name, COUNT(*) as count FROM detections GROUP BY Com_Name) GROUP BY Com_Name ORDER BY AVG(count)")
# Get the species that are in the bottom 10% of this distribution by count
bottom_10=$(echo "$avg_counts" | awk -F'|' -v n="$(echo "$avg_counts" | wc -l)" 'NR <= n * 0.1 {print $1}')
# Remove all directories from $dir_path that aren't in the bottom 10% rarest
find $dir_path -type d | while read dir; do
dir_name=$(basename "$dir")
if ! echo "$bottom_10" | grep -q "$dir_name"; then
#rm -r "$dir"
echo "REMOVING: $dir"
fi
done
Regular → Executable
View File
+38
View File
@@ -0,0 +1,38 @@
#!/bin/bash
# Connect to the database and extract the data
sqlite3 birds.db <<EOF | while IFS='|' read -r id mean_volume recorded_at; do
.mode csv
.headers off
SELECT id, mean_volume, strftime('%Y-%m-%d %H:00:00', recorded_at) AS hour
FROM volume;
done | {
# Initialize variables for the first hour
current_hour=""
total_volume=0
count=0
# Loop through each row of data
while IFS='|' read -r id mean_volume hour; do
if [[ "$hour" != "$current_hour" ]]; then
# If we've moved on to a new hour, output the previous hour's data
if [[ "$current_hour" != "" ]]; then
echo "$id|$((total_volume/count))|$current_hour"
fi
# Reset the variables for the new hour
current_hour="$hour"
total_volume="$mean_volume"
count=1
else
# If we're still in the same hour, add to the total and count
total_volume="$((total_volume + mean_volume))"
count="$((count + 1))"
fi
done
# Output the final hour's data
echo "$id|$((total_volume/count))|$current_hour"
}
EOF
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
# Loop through each entry in birds.db
sqlite3 ~/BirdNET-Pi/scripts/birds.db "SELECT DISTINCT Sci_Name FROM detections" | while read -r row; do
# Get Sci_Name from current row
sci_name=$(echo "$row" | cut -d '|' -f 3)
# Look up ComName for Sci_Name
com_name=$(grep -iE "$sci_name.*" ~/BirdNET-Pi/model/labels.txt | cut -d '_' -f 2-)
if [[ -n "$com_name" ]]; then
# Update Com_Name in birds.db
sqlite3 ~/BirdNET-Pi/scripts/birds.db "UPDATE detections SET Com_Name='$com_name' WHERE Sci_Name='$sci_name'"
echo "SETTING "$com_name" WHERE sciname="$sci_name"\n"
fi
done
Regular → Executable
View File