Merge branch 'main' of github.com:mcguirepr89/BirdNET-Pi

I didn't expect to need to merge?
This commit is contained in:
mcguirepr89
2022-04-02 15:29:34 -04:00
93 changed files with 4738 additions and 7389 deletions
+1 -1
View File
@@ -713,7 +713,7 @@ consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
=============================================================================
Caddy, and Adminer use the Apache License Version 2.0
Caddy, ,Streamlit, and Adminer use the Apache License Version 2.0
=============================================================================
Apache License
Version 2.0, January 2004
+5 -6
View File
@@ -1,5 +1,5 @@
<h1 align="center">
BirdNET-Pi <img src="https://img.shields.io/badge/version-0.11.1-orange" />
BirdNET-Pi <img src="https://img.shields.io/badge/Version-0.13-pink" />
</h1>
<p align="center">
A realtime acoustic bird classification system for the Raspberry Pi 4B
@@ -47,12 +47,11 @@ If your installation isn't in one of the countries listed above, please let me k
* FTP server included
* Automatic extraction of detected data (creating audio clips of detected bird sounds)
* Spectrograms available for all extractions
* MariaDB integration
* NoMachine remote desktop (for personal use only)
* SQLite3 Database
* Live audio stream
* Adminer database maintenance
* Integrated phpSysInfo
* New species mobile notifications from Pushed.co (for iOS users only)
* [phpSysInfo](https://github.com/phpsysinfo/phpsysinfo)
* New species mobile notifications from [Pushed.co](https://pushed.co/quick-start-guide) (for iOS users only)
* Localization supported
## Requirements
@@ -65,7 +64,7 @@ If your installation isn't in one of the countries listed above, please let me k
The system can be installed with:
```
curl -s https://raw.githubusercontent.com/mcguirepr89/BirdNET-Pi/main/newinstaller.sh | bash | tee -a installation.log 2>&1 && sudo reboot
curl -s https://raw.githubusercontent.com/mcguirepr89/BirdNET-Pi/main/newinstaller.sh | bash
```
The installer takes care of any and all necessary updates, so you can run that as the very first command upon the first boot, if you'd like.
+3 -57
View File
@@ -4,13 +4,10 @@
#--------------------- Required: Latitude, and Longitude ----------------------#
## The shell substitution below guesses these based on your network. THESE NEED
## TO BE CHANGED TO STATIC VALUES
## Please only go to 4 decimal places. Example:43.3984
LATITUDE="$(curl -s4 ifconfig.co/json | awk '/lat/ {print $2}' | tr -d ',')"
LONGITUDE="$(curl -s4 ifconfig.co/json | awk '/lon/ {print $2}' | tr -d ',')"
LATITUDE=
LONGITUDE=
#--------------------- BirdWeather Station Information -----------------------#
#_____________The variable below can be set to have your BirdNET-Pi____________#
@@ -28,14 +25,6 @@ BIRDWEATHER_ID=
CADDY_PWD=
#------------------------- MariaDB User Passwords ---------------------------#
#_____________The variable below sets the password for the_____________________#
#_______________________'birder' user on the MariaDB___________________________#
## DB_PWD is for the 'birder' user. The shell substitution below sets a random
## password for you during installation.
DB_PWD=$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo;)
#------------------------- Live Audio Stream --------------------------------#
#_____________The variable below configures/enables the live___________________#
#_____________________________audio stream.____________________________________#
@@ -84,24 +73,6 @@ PUSHED_APP_SECRET=
RECS_DIR=/home/pi/BirdSongs
#------------------------------ Extraction Service ---------------------------#
## DO_EXTRACTIONS is simply a setting for enabling the extraction.service.
## Set this to Y or y to enable extractions.
DO_EXTRACTIONS=y
#----------------------------- Recording Service ----------------------------#
#____________________The variable below can be set to enable __________________#
#________________________the birdnet_recording.service ________________________#
## DO_RECORDING is simply a setting for enabling the 24/7
## birdnet_recording.service.
## Set this to Y or y to enable recording.
DO_RECORDING=y
## REC_CARD is the sound card you would want the birdnet_recording.service to
## use. Leave this as "default" to use PulseAudio (recommended), or use
## the output from "aplay -L" to specify an ALSA device.
@@ -118,12 +89,6 @@ PROCESSED=${RECS_DIR}/Processed
EXTRACTED=${RECS_DIR}/Extracted
## IDFILE is the file that keeps a complete list of every spececies that
## BirdNET has identified from your data-set. It is a relic and not really
## used anymore.
IDFILE=${HOME}/BirdNET-Pi/IdentifiedSoFar.txt
## OVERLAP is the value in seconds which BirdNET should use when analyzing
## the data. The values must be between 0.0-2.9.
@@ -150,11 +115,6 @@ CHANNELS=2
FULL_DISK=purge
## VENV is the python virtual environment wherein all modules and site-packages
## have been installed.
VENV=/home/pi/BirdNET-Pi/birdnet
## RECORDING_LENGTH sets the length of the recording that BirdNET-Lite will
## analyze.
@@ -185,18 +145,4 @@ BIRDNET_USER=pi
## These are just for debugging
LAST_RUN=
THIS_RUN=
## DEPRECATED
#------------------------------- NoMachine ----------------------------------#
#_____________The variable below can be set include NoMachine__________________#
#_________________remote desktop software to be installed._____________________#
# Keep this EMPTY if you do not want to install NoMachine. #
## INSTALL_NOMACHINE is simply a setting that can be enabled to install
## NoMachine alongside the BirdNET-Pi for remote desktop access. This in-
## staller assumes personal use. Please reference the LICENSE file included
## in this repository for more information.
## Set this to Y or y to install NoMachine alongside the BirdNET-Lite
INSTALL_NOMACHINE=y
IDFILE=/home/pi/BirdNET-Pi/IdentifiedSoFar.txt
-60
View File
@@ -1,60 +0,0 @@
<html>
<head>
<title>HTML Frames Example - Content</title>
<style type="text/css">
body {
font-family:verdana,arial,sans-serif;
font-size:10pt;
margin:30px;
background-color:#ffcc00;
}
</style>
</head>
<body>
tbc....
<h1>Thanks</h1>
stefan kahl
patrick mcguire
<h1>Similar Systems</h1>
<h1>Technical Details</h1>
machine learning
deep learning
<h1>Used Tools</h1>
SoX
ffmpeg
Python
Tensorfflow (Lite)
Rasberian OS
<h1>Disclaimer</h1>
<h1>Other Systems</h1>
links to other systems
<h1>Content</h1>
<h2>Replacing the Contents of the Current Frame</h2>
<p>Clicking on these links will open the new page within the current frame.</p>
<ul>
<li><a href="white.html" target="content">Load white page</a></li>
<li><a href="green.html" target="content">Load green page</a></li>
</ul>
<h2>Replacing the Whole Frameset</h2>
<p>When you click on any of the following links, the whole frameset is replaced with the new website. This is because we're using <code>target="_top"</code> in the anchor links.</p>
<ul>
<li><a href="http://www.quackit.com" target="_top">Quackit</a></li>
<li><a href="http://www.quackit.com/html/templates/frames/" target="_top">HTML Frames Templates</a></li>
<li>Learn more about frames with the <a href="http://www.quackit.com/html/tutorial/html_frames.cfm" target="_top">frames tutorial</a><//li>
</ul>
<h2>Open a New Window</h2>
<p>These links open in a new browser window. This is because we use <code>target="_blank"</code>.</p>
<ul>
<li><a href="http://www.code-generator.net" target="_blank">Code Generator</a></li>
<li><a href="http://www.zappyhost.com" target="_blank">ZappyHost</a></li>
<li><a href="http://www.natural-environment.com" target="_blank">Natural Environment</a></li>
<li><a href="http://www.great-workout.com" target="_blank">Great Workout</a></li>
</ul>
</body>
</html>
-22
View File
@@ -1,22 +0,0 @@
<html>
<head>
<title>HTML Frames Example - Footer</title>
<style type="text/css">
body {
font-family:verdana,arial,sans-serif;
font-size:medium;
padding-top: 15px;
text-align:center;
background-color: rgb(119, 196, 135);
color: black;
}
a {
color:black;
}
</style>
</head>
<body>
<a href="/stream" target="footer"><h4>Play Live Audio</h4></a>
</p>
</body>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 869 B

+1 -1
View File
@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="96" height="20" role="img" aria-label="version: 0.11.1"><title>version: 0.11.1</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="96" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="51" height="20" fill="#555"/><rect x="51" width="45" height="20" fill="#fe7d37"/><rect width="96" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="265" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="410">version</text><text x="265" y="140" transform="scale(.1)" fill="#fff" textLength="410">version</text><text aria-hidden="true" x="725" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="350">0.11.1</text><text x="725" y="140" transform="scale(.1)" fill="#fff" textLength="350">0.11.1</text></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="86" height="20" role="img" aria-label="Version: 0.13"><title>Version: 0.13</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="86" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="51" height="20" fill="#555"/><rect x="51" width="35" height="20" fill="pink"/><rect width="86" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="265" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="410">Version</text><text x="265" y="140" transform="scale(.1)" fill="#fff" textLength="410">Version</text><text aria-hidden="true" x="675" y="150" fill="#ccc" fill-opacity=".3" transform="scale(.1)" textLength="250">0.13</text><text x="675" y="140" transform="scale(.1)" fill="#333" textLength="250">0.13</text></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

-42
View File
@@ -1,42 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>BirdNET-Pi</title>
<style>
* {
background-color: background-color: rgb(119, 196, 135);
}
</style>
</head>
<!--
Note the following:
1. Each frame has it's own 'frame' tag.
2. Each frame has a name (eg, name="menu"). This is used for when you need to load one frame from another. For example, your left frame might have links that, when clicked on, loads a new page in the right frame. This is acheived by using 'target="content"' within your links/anchor tags.
3. Each 'frame' tag has a 'src' attribute. This is where you specify the name of the file to be loaded into that frame when the page first loads.
4. You can change the size of the frames by changing the value of the 'cols' and/or 'rows' attribute. A value of "200" sets the frame at 200 pixels. An asterisk (*) specifies that the frame should use up whatever space is left over from the other frames. You can also use percentage values if desired (i.e. 20%,80%)
5. To specify a border, set 'frameborder' and 'border' to "1". I included both attributes for maximum browser compatibility.
6. The 'framespacing' attribute doesn't work in all browsers, but you can set any numeric value you like here.
7. To learn more about HTML frames, check out: http://www.quackit.com/html/tutorial/html_frames.cfm
-->
<script type="text/javascript">
if (screen.width <= 699) {
document.location = "/mobile.html";
}
</script>
<frameset rows="100,*" frameborder="0" border="0" framespacing="0">
<frameset style="background-color: rgb(119, 196, 135);" cols="*,150" frameborder="0" border="0" framespacing="0" />
<frame name="topNav" src="top.html" scrolling="off">
<frame name="footer" src="footer.html" scrolling="off">
</frameset>
<frameset cols="150,*" frameborder="0" border="0" framespacing="0">
<frame name="menu" src="menu.html" marginheight="0" marginwidth="0" scrolling="auto" noresize>
<frame name="content" src="/overview.php" marginheight="0" marginwidth="0" scrolling="auto" noresize>
<noframes>
<p>This section (everything between the 'noframes' tags) will only be displayed if the users' browser doesn't support frames. You can provide a link to a non-frames version of the website here. Feel free to use HTML tags within this section.</p>
</noframes>
</frameset>
</html>
+66
View File
@@ -0,0 +1,66 @@
<title>BirdNET-Pi</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body::-webkit-scrollbar {
display:none
}
</style>
<link rel="stylesheet" href="style.css">
<body>
<div class="banner">
<div class="logo">
<?php if(isset($_GET['logo'])) {
echo "<a href=\"https://github.com/mcguirepr89/BirdNET-Pi.git\" target=\"_blank\"><img style=\"width:60;height:60;\" src=\"images/bird.png\"></a>";
} else {
echo "<a href=\"https://github.com/mcguirepr89/BirdNET-Pi.git\" target=\"_blank\"><img src=\"images/bird.png\"></a>";
}?>
</div>
<div class="stream">
<?php
if(isset($_GET['stream'])){
if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/thisrun.txt');
} elseif (file_exists('/home/pi/BirdNET-Pi/firstrun.ini')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/firstrun.ini');
}
$caddypwd = $config['CADDY_PWD'];
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You cannot listen to the live audio stream';
exit;
} else {
$submittedpwd = $_SERVER['PHP_AUTH_PW'];
$submitteduser = $_SERVER['PHP_AUTH_USER'];
if($submittedpwd == $caddypwd && $submitteduser == 'birdnet'){
echo "
<audio controls autoplay><source src=\"/stream\"></audio>
</div>
<h1><a href=\"\">BirdNET-Pi</a><img src=\"images/version.svg\"></h1>
</div>";
} else {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You cannot listen to the live audio stream';
exit;
}
}
} else {
echo "
<form action=\"\" method=\"GET\">
<button type=\"submit\" name=\"stream\" value=\"play\">Live Audio</button>
</form>
</div>
<h1><a href=\"\">BirdNET-Pi</a><img src=\"images/version.svg\"></h1>
</div>";
}
if(isset($_GET['log'])) {
if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
header("Location: /log");}
} elseif(isset($_GET['spectrogram'])){
header("Location: /spectrogram.php");
} else {
echo "
<iframe src=\"/views.php\">
</div>";
}
-79
View File
@@ -1,79 +0,0 @@
<html>
<head>
<title>HTML Frames Example - Menu</title>
<style type="text/css">
a {
text-decoration:none;
margin: 0px 0px 0px 0px;
color:black;
font-size: small;
}
body {
font-family:verdana,arial,sans-serif;
font-size:medium;
margin:10px;
background-color: rgb(119, 196, 135);
text-align: left;
padding-bottom: 30px;
}
h3 {
margin: 0px 0px -15px 0px;
text-align: center;
}
h4,h5 {
text-align: left;
margin: 0px 0px 5px 0px;
}
footer {
background: rgb(119, 196, 135);
position: fixed;
padding-top: 10px;
left: 0;
bottom: 0;
width:100%;
}
hr {
border-bottom: 1px solid black;
}
body::-webkit-scrollbar {
display:none
}
</style>
</head>
<body>
<h5>Extractions</h5>
- <a href="/By_Date/" target="content">By Date</a><br>
- <a href="/By_Common_Name/" target="content">By Common Name</a><br>
- <a href="/By_Scientific_Name/" target="content">By Scientific Name</a><br>
- <a href="/Processed/" target="content">Processed</a><br>
<hr class="solid">
<h5>System</h5>
- <a href="/scripts/" target="content">Tools</a><br>
- <a href="/phpsysinfo/" target="content">Info</a><br>
- <a href="http://birdnetpi.local:8080" target="content">Log</a><br>
<hr class="solid">
<h5>External</h5>
- <a href="https://app.birdweather.com" target="_content">BirdWeather</a><br>
- <a href="https://github.com/mcguirepr89/BirdNET-Pi" target="_content">Repository</a><br>
- <a href="https://github.com/mcguirepr89/BirdNET-Pi/wiki" target="_content">Wiki Help Page</a><br>
- <a href="https://v2.wttr.in/" target="content">Wttr.in Report</a><br>
- <a href="https://birdnet.cornell.edu" target="_content">BirdNET</a><br>
<hr class="solid">
<h5>Other<br>BirdNET-Pis</h5>
- <a href="https://birdnetpi.pmcgui.xyz" target="top">United States</a><br>
- <a href="https://birds.naturestation.net" target="top">South Africa</a><br>
- <a href="https://birdnet.svardsten.se" target="top">Sweden</a><br>
- <a href="https://birdnetgv.ddnss.de" target="top">Germany</a>
</body>
<footer style="font-size: small;">
Icon by <a target="_content" style="font-weight: bold" href="https://www.freepik.com" title="Freepik">Freepik</a> from <a target="_content" style="font-weight: bold" href="https://www.flaticon.com/" title="Flaticon">Flaticon.com</a>
</footer>
</html>
-80
View File
@@ -1,80 +0,0 @@
<html>
<head>
<title>Menu</title>
<style type="text/css">
a {
text-decoration:none;
margin: 0px 0px 0px 0px;
color:black;
font-size: small;
}
body {
font-family:verdana,arial,sans-serif;
font-size:medium;
margin:10px;
background-color: rgb(119, 196, 135);
text-align: left;
padding-bottom: 30px;
}
h3 {
margin: 0px 0px -15px 0px;
text-align: center;
}
h4,h5 {
text-align: center;
margin: 0px 0px 5px 0px;
}
footer {
background: rgb(119, 196, 135);
position: fixed;
padding-top: 10px;
left: 0;
bottom: 0;
width:100%;
}
hr {
border-bottom: 1px solid black;
}
body::-webkit-scrollbar {
display:none
}
</style>
</head>
<body>
<h5>Extraktionen</h5>
- <a href="https://birdnetpi.local/By_Date/" target="content">Nach Datum</a><br>
- <a href="https://birdnetpi.local/By_Common_Name/" target="content">Nach Namen</a><br>
- <a href="https://birdnetpi.local/By_Scientific_Name/" target="content">Nach wiss. Namen</a><br>
- <a href="https://birdnetpi.local/Processed/" target="content">Verarbeitet</a><br>
<hr class="solid">
<h5>System-Links</h5>
- <a href="https://birdnetpi.local/scripts/" target="content">Tools</a><br>
- <a href="https://birdnetpi.local/phpsysinfo/" target="content">System Info</a><br>
- <a href="https://birdnetpi.local:8080" target="content">BirdNET-Lite Log</a><br>
- <a href="https://birdnetpi.local:8888" target="content">Extraction Log</a><br>
<hr class="solid">
<h5>Externe Links</h5>
- <a href="https://app.birdweather.com" target="_content">BirdWeather</a><br>
- <a href="https://github.com/mcguirepr89/BirdNET-Pi" target="_content">Repository</a><br>
- <a href="https://github.com/mcguirepr89/BirdNET-Pi/wiki" target="_content">Wiki Hilfe-Seite</a><br>
- <a href="https://v2.wttr.in/" target="content">Wttr.in Report</a><br>
- <a href="https://birdnet.cornell.edu" target="_content">BirdNET @ Cornell</a><br>
<hr class="solid">
<h5>Andere<br>BirdNET-Pis</h5>
- <a href="https://birdnetpi.pmcgui.xyz" target="_content">United States</a><br>
- <a href="https://birds.naturestation.net" target="_content">South Africa</a><br>
- <a href="https://birdnet.svarsten.se" target="_content">Sweden</a>
- <a href="https://birdnetgv.ddnss.de" target="_content">Germany</a>
</body>
<footer style="font-size: small;">
Icon by <a target="_content" style="font-weight: bold" href="https://www.freepik.com" title="Freepik">Freepik</a> from <a target="_content" style="font-weight: bold" href="https://www.flaticon.com/" title="Flaticon">Flaticon.com</a>
</footer>
</html>
-509
View File
@@ -1,509 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>BirdNET-Pi</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* { padding: 0; margin: 0; }
body {
font-family: sans-serif;
text-rendering: optimizespeed;
background-color: #ffffff;
}
a {
color: #006ed3;
text-decoration: none;
}
a:hover,
h1 a:hover {
color: #319cff;
}
header,
#summary {
padding-left: 5%;
padding-right: 5%;
}
th:first-child,
td:first-child {
width: 5%;
}
th:last-child,
td:last-child {
width: 5%;
}
header {
padding-top: 25px;
padding-bottom: 10px;
background-color: #eef;
font-family: Georgia, serif;
}
h1 {
font-size: 34px;
font-weight: normal;
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
color: #999;
}
h2 {
padding-left: 5%;
font-size: 18px;
margin-bottom: 8px;
margin-top: 12px;
font-weight: normal;
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
color: #999;
}
h1 a {
color: #005;
margin: 0 4px;
}
h1 a:hover {
text-decoration: underline;
}
h1 a:first-child {
margin: 0;
}
.meta {
font-size: 12px;
font-family: Trebuchet, sans-serif;
border-bottom: 1px solid #9C9C9C;
padding-top: 10px;
padding-bottom: 10px;
}
.meta-item {
margin-right: 1em;
}
#filter {
padding: 4px;
border: 1px solid #CCC;
}
table {
width: 100%;
border-collapse: collapse;
}
tr {
border-bottom: 1px dashed #dadada;
}
tbody tr:hover {
background-color: #ffffec;
}
th,
td {
text-align: left;
padding: 10px 0;
}
th {
padding-top: 15px;
padding-bottom: 15px;
font-size: 16px;
white-space: nowrap;
}
th a {
color: black;
}
th svg {
vertical-align: middle;
}
td {
white-space: nowrap;
font-size: 14px;
}
td:nth-child(2) {
width: 100%;
}
td:nth-child(3),
th:nth-child(3) {
#padding: 0 20px 0 20px;
width: 0px;
}
th:nth-child(4),
td:nth-child(4) {
text-align: right;
}
td:nth-child(2) svg {
position: absolute;
}
td .name,
td .goup {
margin-left: 1.75em;
word-break: break-all;
overflow-wrap: break-word;
white-space: pre-wrap;
}
.icon {
margin-right: 5px;
}
.icon.sort {
display: inline-block;
width: 1em;
height: 1em;
position: relative;
top: .2em;
}
.icon.sort .top {
position: absolute;
left: 0;
top: -1px;
}
.icon.sort .bottom {
position: absolute;
bottom: -1px;
left: 0;
}
footer {
padding: 40px 20px;
font-size: 12px;
text-align: center;
}
@media (max-width: 600px) {
.hideable {
display: none;
}
td:nth-child(2) {
width: auto;
}
th:nth-child(3),
td:nth-child(3) {
padding-right: 5%;
text-align: right;
}
h1 {
color: #000;
}
h1 a {
margin: 0;
}
#filter {
max-width: 100px;
}
}
@media (prefers-color-scheme: dark) {
body {
background-color: #101010;
color: #dddddd;
}
header {
background-color: #151515;
}
tbody tr:hover {
background-color: #252525;
}
header a,
th a {
color: #dddddd;
}
a {
color: #5796d1;
text-decoration: none;
}
a:hover,
h1 a:hover {
color: #62b2fd;
}
tr {
border-bottom: 1px dashed rgba(255, 255, 255, 0.15);
}
#up-arrow,
#down-arrow {
fill: #dddddd;
}
#filter {
background-color: #151515;
color: #ffffff;
border: 1px solid #212121;
}
.meta {
border-bottom: 1px solid #212121
}
}
</style>
</head>
<body onload='initFilter()'>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="0" width="0" style="position: absolute;">
<defs>
<!-- Folder -->
<g id="folder" fill-rule="nonzero" fill="none">
<path d="M285.22 37.55h-142.6L110.9 0H31.7C14.25 0 0 16.9 0 37.55v75.1h316.92V75.1c0-20.65-14.26-37.55-31.7-37.55z" fill="#FFA000"/>
<path d="M285.22 36H31.7C14.25 36 0 50.28 0 67.74v158.7c0 17.47 14.26 31.75 31.7 31.75H285.2c17.44 0 31.7-14.3 31.7-31.75V67.75c0-17.47-14.26-31.75-31.7-31.75z" fill="#FFCA28"/>
</g>
<g id="folder" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="folder-group" fill-rule="nonzero">
<g id="folder-shape">
<path d="M285.224876,37.5486902 L142.612438,37.5486902 L110.920785,0 L31.6916529,0 C14.2612438,0 0,16.8969106 0,37.5486902 L0,112.646071 L316.916529,112.646071 L316.916529,75.0973805 C316.916529,54.4456008 302.655285,37.5486902 285.224876,37.5486902 Z" id="Shape" fill="#FFA000"></path>
<path d="M285.224876,36 L31.6916529,36 C14.2612438,36 0,50.2838568 0,67.7419039 L0,226.451424 C0,243.909471 14.2612438,258.193328 31.6916529,258.193328 L285.224876,258.193328 C302.655285,258.193328 316.916529,243.909471 316.916529,226.451424 L316.916529,67.7419039 C316.916529,50.2838568 302.655285,36 285.224876,36 Z" id="Shape" fill="#FFCA28"></path>
</g>
<path d="M126.154134,250.559184 C126.850974,251.883673 127.300549,253.006122 127.772602,254.106122 C128.469442,255.206122 128.919016,256.104082 129.638335,257.002041 C130.559962,258.326531 131.728855,259 133.100057,259 C134.493737,259 135.415364,258.55102 136.112204,257.67551 C136.809044,257.002041 137.258619,255.902041 137.258619,254.577551 C137.258619,253.904082 137.258619,252.804082 137.033832,251.457143 C136.786566,249.908163 136.561779,249.032653 136.561779,248.583673 C136.089726,242.814286 135.864939,237.920408 135.864939,233.273469 C135.864939,225.057143 136.786566,217.514286 138.180246,210.846939 C139.798713,204.202041 141.889234,198.634694 144.429328,193.763265 C147.216689,188.869388 150.678411,184.873469 154.836973,181.326531 C158.995535,177.779592 163.626149,174.883673 168.481552,172.661224 C173.336954,170.438776 179.113983,168.665306 185.587852,167.340816 C192.061722,166.218367 198.760378,165.342857 205.481514,164.669388 C212.18017,164.220408 219.598146,163.995918 228.162535,163.995918 L246.055591,163.995918 L246.055591,195.514286 C246.055591,197.736735 246.752431,199.510204 248.370899,201.059184 C250.214153,202.608163 252.079886,203.506122 254.372715,203.506122 C256.463236,203.506122 258.531277,202.608163 260.172223,201.059184 L326.102289,137.797959 C327.720757,136.24898 328.642384,134.47551 328.642384,132.253061 C328.642384,130.030612 327.720757,128.257143 326.102289,126.708163 L260.172223,63.4469388 C258.553756,61.8979592 256.463236,61 254.395194,61 C252.079886,61 250.236632,61.8979592 248.393377,63.4469388 C246.77491,64.9959184 246.07807,66.7693878 246.07807,68.9918367 L246.07807,100.510204 L228.162535,100.510204 C166.863084,100.510204 129.166282,117.167347 115.274437,150.459184 C110.666301,161.54898 108.350993,175.310204 108.350993,191.742857 C108.350993,205.279592 113.903236,223.912245 124.760454,247.438776 C125.00772,248.112245 125.457294,249.010204 126.154134,250.559184 Z" id="Shape" fill="#FFFFFF" transform="translate(218.496689, 160.000000) scale(-1, 1) translate(-218.496689, -160.000000) "></path>
</g>
</g>
<!-- File -->
<g id="file" stroke="#000" stroke-width="25" fill="#FFF" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<path d="M13 24.12v274.76c0 6.16 5.87 11.12 13.17 11.12H239c7.3 0 13.17-4.96 13.17-11.12V136.15S132.6 13 128.37 13H26.17C18.87 13 13 17.96 13 24.12z"/>
<path d="M129.37 13L129 113.9c0 10.58 7.26 19.1 16.27 19.1H249L129.37 13z"/>
</g>
<g id="file-shortcut" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="file-shortcut-group" transform="translate(13.000000, 13.000000)">
<g id="file-shortcut-shape" stroke="#000000" stroke-width="25" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round">
<path d="M0,11.1214886 L0,285.878477 C0,292.039924 5.87498876,296.999983 13.1728373,296.999983 L225.997983,296.999983 C233.295974,296.999983 239.17082,292.039942 239.17082,285.878477 L239.17082,123.145388 C239.17082,123.145388 119.58541,2.84217094e-14 115.369423,2.84217094e-14 L13.1728576,2.84217094e-14 C5.87500907,-1.71479982e-05 0,4.96022995 0,11.1214886 Z" id="rect1171"></path>
<path d="M116.37005,0 L116,100.904964 C116,111.483663 123.258008,120 132.273377,120 L236,120 L116.37005,0 L116.37005,0 Z" id="rect1794"></path>
</g>
<path d="M47.803141,294.093878 C48.4999811,295.177551 48.9495553,296.095918 49.4216083,296.995918 C50.1184484,297.895918 50.5680227,298.630612 51.2873415,299.365306 C52.2089688,300.44898 53.3778619,301 54.7490634,301 C56.1427436,301 57.0643709,300.632653 57.761211,299.916327 C58.4580511,299.365306 58.9076254,298.465306 58.9076254,297.381633 C58.9076254,296.830612 58.9076254,295.930612 58.6828382,294.828571 C58.4355724,293.561224 58.2107852,292.844898 58.2107852,292.477551 C57.7387323,287.757143 57.5139451,283.753061 57.5139451,279.95102 C57.5139451,273.228571 58.4355724,267.057143 59.8292526,261.602041 C61.44772,256.165306 63.5382403,251.610204 66.0783349,247.62449 C68.8656954,243.620408 72.3274172,240.35102 76.4859792,237.44898 C80.6445412,234.546939 85.2751561,232.177551 90.1305582,230.359184 C94.9859603,228.540816 100.76299,227.089796 107.236859,226.006122 C113.710728,225.087755 120.409385,224.371429 127.13052,223.820408 C133.829177,223.453061 141.247152,223.269388 149.811542,223.269388 L167.704598,223.269388 L167.704598,249.057143 C167.704598,250.87551 168.401438,252.326531 170.019905,253.593878 C171.86316,254.861224 173.728893,255.595918 176.021722,255.595918 C178.112242,255.595918 180.180284,254.861224 181.82123,253.593878 L247.751296,201.834694 C249.369763,200.567347 250.291391,199.116327 250.291391,197.297959 C250.291391,195.479592 249.369763,194.028571 247.751296,192.761224 L181.82123,141.002041 C180.202763,139.734694 178.112242,139 176.044201,139 C173.728893,139 171.885639,139.734694 170.042384,141.002041 C168.423917,142.269388 167.727077,143.720408 167.727077,145.538776 L167.727077,171.326531 L149.811542,171.326531 C88.5120908,171.326531 50.8152886,184.955102 36.9234437,212.193878 C32.3153075,221.267347 30,232.526531 30,245.971429 C30,257.046939 35.5522422,272.291837 46.4094607,291.540816 C46.6567266,292.091837 47.1063009,292.826531 47.803141,294.093878 Z" id="Shape-Copy" fill="#000000" fill-rule="nonzero" transform="translate(140.145695, 220.000000) scale(-1, 1) translate(-140.145695, -220.000000) "></path>
</g>
</g>
<!-- Up arrow -->
<g id="up-arrow" transform="translate(-279.22 -208.12)">
<path transform="matrix(.22413 0 0 .12089 335.67 164.35)" stroke-width="0" d="m-194.17 412.01h-28.827-28.827l14.414-24.965 14.414-24.965 14.414 24.965z"/>
</g>
<!-- Down arrow -->
<g id="down-arrow" transform="translate(-279.22 -208.12)">
<path transform="matrix(.22413 0 0 -.12089 335.67 257.93)" stroke-width="0" d="m-194.17 412.01h-28.827-28.827l14.414-24.965 14.414-24.965 14.414 24.965z"/>
</g>
</defs>
</svg>
<header>
<h1>
<a href="https://github.com/mcguirepr89/BirdNET-Lite">🕊 BirdNET-Pi</a>
</h1>
</header>
<hr>
<h2>
<a href="/overview.php">
<span class="name">🐦 Overview</span>
</a>
</h2>
<hr style="border-top: dotted 2px #ccc;">
<h2>
<a href="/viewday.php">
<span class="name">🐦 Today's Top 10</span>
</a>
</h2>
<hr style="border-top: dotted 2px #ccc;">
<h2>
<a href="/viewdb.php">
<span class="name">🐦 View the Database</span>
</a>
</h2>
<hr style="border-top: dotted 2px #ccc;">
<h2>
<a href="./stream">
<span class="name">🔊 Live Audio Stream</span>
</a>
</h2>
<hr style="border-top: dotted 2px #ccc;">
<main>
<div class="listing">
<table aria-describedby="summary">
<thead>
<tr>
<th></th>
<th>
<a href="?sort=name&order=asc"><p style="color:#555">Extractions</p></a>
</th>
</tr>
</thead>
<tbody>
<tr class="file">
<td></td>
<td>
<a href="./By_Common_Name/">
<svg width="1.5em" height="1em" version="1.1" viewBox="0 0 317 259"><use xlink:href="#folder"></use></svg>
<span class="name">By Common Name</span>
</a>
</td>
</tr>
<tr class="file">
<td></td>
<td>
<a href="./By_Date/">
<svg width="1.5em" height="1em" version="1.1" viewBox="0 0 317 259"><use xlink:href="#folder"></use></svg>
<span class="name">By Date</span>
</a>
</td>
</tr>
<tr class="file">
<td></td>
<td>
<a href="./By_Scientific_Name/">
<svg width="1.5em" height="1em" version="1.1" viewBox="0 0 317 259"><use xlink:href="#folder"></use></svg>
<span class="name">By Scientific Name</span>
</a>
</td>
</tr>
<tr class="file">
<td></td>
<td>
<a href="./Processed/">
<svg width="1.5em" height="1em" version="1.1" viewBox="0 0 317 259"><use xlink:href="#folder"></use></svg>
<span class="name">Processed</span>
</a>
</td>
</tr>
</tbody>
<thead>
<tr>
<th></th>
<th>
<a href="?sort=name&order=asc"><p style="color:#555">Tools</p></a>
</th>
</tr>
</thead>
<tr class="file">
<td></td>
<td>
<a href="./scripts/">
<svg width="1.5em" height="1em" version="1.1" viewBox="0 0 317 259"><use xlink:href="#folder"></use></svg>
<span class="name">🗎 Scripts</span>
</a>
</td>
</tr>
<tr class="file">
<td></td>
<td>
<a href="http://birdnetpi.local:8080">
<svg width="1.5em" height="1em" version="1.1" viewBox="0 0 317 259"><use xlink:href="#folder"></use></svg>
<span class="name">⚙ Analysis Log</span>
</a>
</td>
</tr>
<tr class="file">
<td></td>
<td>
<a href="http://birdnetpi.local:8888">
<svg width="1.5em" height="1em" version="1.1" viewBox="0 0 317 259"><use xlink:href="#folder"></use></svg>
<span class="name">⚝ Extraction Log</span>
</a>
</td>
</tr>
<tr>
<td></td>
<td>
<a href="./phpsysinfo">
<span class="name">System Information</span>
</a>
</td>
</tr>
<tr class="file">
<td></td>
<td>
<form action="/spectrogram.php">
<input type="submit" value="Spectrogram View">
</form>
</td>
</tbody>
</table>
</div>
</main>
<footer>
Help:<a rel="noopener noreferrer" href="https://github.com/mcguirepr89/BirdNET-Pi/wiki/">🕊 BirdNET-Pi Wiki</a>
</footer>
<script>
var filterEl = document.getElementById('filter');
filterEl.focus();
function initFilter() {
if (!filterEl.value) {
var filterParam = new URL(window.location.href).searchParams.get('filter');
if (filterParam) {
filterEl.value = filterParam;
}
}
filter();
}
function filter() {
var q = filterEl.value.trim().toLowerCase();
var elems = document.querySelectorAll('tr.file');
elems.forEach(function(el) {
if (!q) {
el.style.display = '';
return;
}
var nameEl = el.querySelector('.name');
var nameVal = nameEl.textContent.trim().toLowerCase();
if (nameVal.indexOf(q) !== -1) {
el.style.display = '';
} else {
el.style.display = 'none';
}
});
}
function localizeDatetime(e, index, ar) {
if (e.textContent === undefined) {
return;
}
var d = new Date(e.getAttribute('datetime'));
if (isNaN(d)) {
d = new Date(e.textContent);
if (isNaN(d)) {
return;
}
}
e.textContent = d.toLocaleString([], {day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit"});
}
var timeList = Array.prototype.slice.call(document.getElementsByTagName("time"));
timeList.forEach(localizeDatetime);
</script>
</body>
</html>
+463 -70
View File
@@ -1,96 +1,489 @@
/* style sheet */
* {
font-family: 'Arial', 'Gill Sans', 'Gill Sans MT',
' Calibri', 'Trebuchet MS', 'sans-serif';
box-sizing: border-box;
font-size: medium;
}
/* Create two equal columns that floats next to each other */
.column2 {
margin-left: auto;
margin-right: auto;
width: 75%;
padding: 5px;
a {
text-decoration: none;
}
/* Create two equal columns that floats next to each other */
.column {
float: left;
width: 50%;
padding: 5px;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
table {
background-color: rgb(219, 255, 235);
margin: 0 auto;
font-size: large;
border-collapse: collapse;
border-spacing: 0;
width: 100%;
border: 1px solid black;
}
h1 {
text-align: center;
color: black;
font-size: xx-large;
}
h2 {
margin-left: -150px;
text-align: center;
color: black;
font-size: large;
a:hover {
font-weight: bold;
}
h3 {
text-align: center;
color: black;
font-size: large;
}
iframe {
padding: 0;
margin: 0;
border: none;
height: 93%;
width: 100%;
}
body {
margin: 0;
background-color: rgb(119, 196, 135);
}
table {
background-color: transparent;
border-collapse: collapse;
border-spacing: 0;
margin-left: auto;
margin-right: auto;
}
td {
padding: 10px;
vertical-align: top;
background-color: rgb(219, 255, 235);
font-weight: lighter;
border: 1px solid black;
padding: 10px;
border: 2px solid black;
text-align: center;
}
th {
background-color: rgb(219, 255, 235);
font-weight: bold;
border: 1px solid black;
padding: 10px;
height: auto;
text-align: center;
}
@media screen and (max-width: 600px) {
h2 {
margin-left: 0;
text-align: center;
color: black;
font-size: large;
}
.column {
width: 100%;
}
/* Create two equal columns that floats next to each other */
.column2 {
float: center;
width: 100%;
padding: 0px;
}
audio, video{
max-height: 100%;
max-width: 100%;
}
label {
font-weight: bold;
}
button {
background-color: transparent;
border: none;
color: black;
cursor: pointer;
}
button:hover {
font-weight: bold;
color: blue;
}
.row {
display: flex;
}
.centered {
text-align: center;
display: block;
width: auto;
margin-left: auto;
margin-right: auto;
}
.banner {
height: 7%;
text-align: center;
}
.banner h1 {
padding: .5em;
letter-spacing: 5px;
}
.banner audio,.banner form {
float: right;
width: 120px;
margin-left: -120px;
margin-right: auto;
}
.banner button {
padding-top: 8px;
font-weight: bold;
letter-spacing: 1px;
}
.banner a {
text-decoration: none;
color: black;
font-size: x-large;
}
.logo img {
position: absolute;
top: 0;
left: 0;
padding: 10px;
}
.topnav {
background-color: rgb(159, 226, 155);
display: flex;
flex: 65%;
width: 65%;
justify-content: space-between;
margin-left: auto;
margin-right: auto;
}
.topnav form {
margin: 0;
padding: 0;
}
.topnav button {
background-color: transparent;
text-align: center;
padding: 14px 16px;
width: auto;
height: auto;
}
.topnav button:hover {
background-color: rgb(219, 295, 235);
color: black;
}
.topnav button.active {
background-color: #04AA6D;
color: white;
}
.topnav .icon {
display: none;
}
.play table {
margin-left: auto;
margin-right: auto;
}
.stats table,.viewdb table {
width: 75%;
}
.overview th {
background-color: rgb(219, 255, 235);
border: 2px solid black;
text-align: center;
padding: 12px;
}
.overview td {
vertical-align: middle;
}
.overview div img {
max-height: 100%;
display: flex;
justify-content: center;
margin-right: auto;
margin-left: auto;
}
.overview-stats {
display: flex;
justify-content: center;
align-items: baseline;
}
.left-column {
flex: 10%;
padding-left: 10px;
}
.right-column {
flex: 90%;
margin-right: 10%;
}
.stats td {
vertical-align: middle;
}
.stats table {
height: auto;
}
.stats button:hover {
color: blue;
}
.overview button, .center button{
font-weight: bold;
color: blue;
}
.history table,.history img {
width: auto;
margin-left: auto;
margin-right: auto;
}
.views .centered button {
border: 2px solid black;
background-color: rgb(219, 255, 235);
padding: 12px;
}
.views .centered button:hover {
background-color: rgb(159, 226, 155);
color: black;
}
.settings {
padding: 12px;
}
.settings h3 {
text-align: left;
}
.settings button {
border: 2px solid black;
background-color: rgb(219, 255, 235);
padding: 12px;
}
.settings button:hover {
background-color: rgb(159, 226, 155);
color: black;
}
.customlabels,.customlabels2 {
float:left;
}
.customlabels table {
height: 100%;
}
.customlabels td,.customlabels2 td {
border:none;
background-color: transparent;
vertical-align: middle;
}
.customlabels button,.customlabels2 button {
padding: 12px;
background-color: rgb(219, 255, 235);
border: 2px solid black;
width: 100%;
}
.column1, .column3 {
width: 45%;
}
.smaller {
width: 100%;
display: none;
margin-left: auto;
margin-right: auto;
}
.column2 {
text-align: center;
width: 10%;
height: 80%;
}
.column4 {
text-align: justify;
width: 10%;
height: 50%;
}
.column1 form,.column3 form {
width: 80%;
margin-left: auto;
margin-right: auto;
}
.column1 select,.column3 select {
height: 80%;
width: 100%;
}
.spectrogram {
width:50%
}
.full {
width:100%;
}
.logbutton, .navbuttons {
float: left;
}
.systemcontrols form,.servicecontrols form {
text-align: center;
}
.systemcontrols button,.servicecontrols button {
border: 2px solid black;
background-color: rgb(219, 255, 235);
padding: 12px;
width: 50%;
}
.servicecontrols button {
width: 20%;
}
#body::-webkit-scrollbar {
# display:none
#}
@media screen and (max-width: 1290px) {
.column1,.column2,.column3,.column4 {
height: 90%
}
.left-column {
display: none;
}
.right-column {
flex: 100%;
margin: 0;
}
img {
max-width: 100%;
}
.overview {
overflow-x: hidden;
}
.overview .right-column .chart img {
margin-left: 5%;
margin-right: auto;
}
}
@media screen and (max-width: 1000px) {
.customlabels form,.customlabels2 form {
width: 95%;
}
.column1, .column3 {
width: 50%;
height: 100%;
}
.column1 select,.column3 select {
height: 70%;
}
.column2,.column4 {
display: none;
}
.smaller{
display: block;
}
.systemcontrols button,.servicecontrols button {
width: 60%;
padding: 12px;
background-color: rgb(219, 255, 235);
}
.topnav {
flex: 100%;
width: 100%;
}
.topnav button {display: none;}
.topnav button.icon {
padding: 0;
margin: 0;
display: block;
width: 100%;
margin-left: auto;
margin-right: auto;
}
.banner {
height: auto;
}
.banner img {
display: none;
}
.logo img {
display: block;
width: 60px;
height: 60px;
}
.version {
display: none;
}
.topnav.responsive {position: relative;}
.topnav.responsive button {
display: block;
text-align: center;
}
}
@media screen and (max-width: 800px) {
.column1, .column3 {
width: 100%;
}
.systemcontrols button,.servicecontrols button {
width: 80%;
padding: 12px;
background-color: rgb(219, 255, 235);
}
.stats img {
width: 100%;
margin-left:auto;
margin-rigth:auto;
}
.viewdb table,.overview img {
width: 100%
}
.banner {
height: auto;
margin-left: 60px;
}
.banner img {
display: none;
}
.stream {
float: right;
display: block;
width: 60px;
}
.logo img {
display: block;
width: 60px;
height: 60px;
}
.play table,.overview table,.stats table {
width: 100%;
}
.topnav {
flex: 100%;
width: 100%;
flex-direction: column;
}
.topnav button {
font-size: large;
width: 100%
}
.topnav button {display: none;}
.topnav button.icon {
margin: 0;
padding: 0;
display: block;
width: 100%;
margin-left: auto;
margin-right: auto;
}
.topnav.responsive {position: relative;}
.topnav.responsive button {
display: block;
}
.left-column {
display: none;
}
.version {display:none;}
.left {
display:none;
}
}
body::-webkit-scrollbar {
display:none
-51
View File
@@ -1,51 +0,0 @@
<html>
<head>
<title>BirdNET-Pi</title>
<style type="text/css">
a {
font-weight:bold;
text-decoration:none;
color:black;
}
body {
text-align:center;
font-family:verdana,arial,sans-serif;
font-size:medium;
margin:10px;
background-color: rgb(119, 196, 135);
}
#content {
position: relative;
}
#content img {
position: absolute;
top: 0px;
left: 0px;
padding-top: 5px;
padding-left: 10px;
}
footer {
position: absolute;
bottom: 0px;
left: 0px;
right: 0px;
width: 100%;
}
</style>
</head>
<body>
<a href="https://github.com/mcguirepr89/BirdNET-Pi" target="_content">
<img alt="BirdNET-Pi" src="images/bird.png" style="position:absolute;left:32.5px;top:15px;">
</a>
<h1 style="padding-left:150px;margin:0;">BirdNET-Pi</h1>
<img style="padding-left: 150px;margin:0;" src="images/version.svg"/>
<p style="margin:0;padding-left:150px;padding-top:5px">
<a href="/overview.php" target="content">Overview</a> |
<a href="/viewdb.php" target="content">Database</a> |
<a href="/stats.php" target="content">Species Stats</a> |
<a href="/history.php" target="content">History</a> |
<a href="/spectrogram.php" target="content">Spectrogram</a>
</p>
</body>
</html>
-51
View File
@@ -1,51 +0,0 @@
<html>
<head>
<title>BirdNET-Pi</title>
<style type="text/css">
a {
font-weight:bold;
text-decoration:none;
color:black;
}
body {
text-align:center;
font-family:verdana,arial,sans-serif;
font-size:medium;
margin:10px;
background-color: rgb(119, 196, 135);
}
#content {
position: relative;
}
#content img {
position: absolute;
top: 0px;
left: 0px;
padding-top: 5px;
padding-left: 10px;
}
footer {
position: absolute;
bottom: 0px;
left: 0px;
right: 0px;
width: 100%;
}
</style>
</head>
<body>
<a href="https://github.com/mcguirepr89/BirdNET-Pi" target="_content">
<img alt="BirdNET-Pi" src="images/bird.png" style="position:absolute;left:32.5px;top:15px;">
</a>
<h1 style="padding-left:150px">BirdNET-Pi<img style="padding-left: 5px" src="images/version.svg" class="ribbon"/></h1>
</div>
<p style="padding-left:150px;">
<a href="/overview.php" target="content">Übersicht</a> |
<a href="/viewdb.php" target="content">Datenbankansicht</a> |
<a href="/viewday.php" target="content">Tagesansicht</a> |
<a href="/spectrogram.php" target="content">Spektrogrammansicht</a>
</p>
</body>
</html>
+189
View File
@@ -0,0 +1,189 @@
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
<div class="topnav" id="myTopnav">
<form action="" method="POST" id="views">
<button type="submit" name="view" value="Overview" form="views">Overview</button>
</form>
<form action="" method="POST" id="views">
<button type="submit" name="view" value="Today's Detections" form="views">Today's Detections</button>
</form>
<form action="" method="POST" id="views">
<button type="submit" name="view" value="Best Recordings" form="views">Best Recordings</button>
</form>
<form action="" method="POST" id="views">
<button type="submit" name="view" value="Species Stats" form="views">Species Stats</button>
</form>
<form action="" method="POST" id="views">
<button type="submit" name="view" value="Daily Charts" form="views">Daily Charts</button>
</form>
<form action="" method="POST" id="views">
<button type="submit" name="view" value="Recordings" form="views">Recordings</button>
</form>
<form action="index.php" method="GET" id="spectrogram">
<input type="hidden" name="logo" value="smaller">
<button style="float:none;" type="submit" name="spectrogram" value="view" id="spectrogram" form="spectrogram">Spectrogram</button>
</form>
<form action="index.php" method="GET" id="Log">
<input type="hidden" name="logo" value="smaller">
<button type="submit" name="log" value="log" form="Log">View Log</button>
</form>
<form action="" method="POST" id="views">
<button type="submit" name="view" value="Tools" form="views">Tools</button>
</form>
<button href="javascript:void(0);" class="icon" onclick="myFunction()"><img src="images/menu.png"></button>
</div>
<div class="views">
<?php
if(isset($_POST['view'])){
if($_POST['view'] == "System Info"){header('location:phpsysinfo/index.php');}
if($_POST['view'] == "System Controls"){include('scripts/system_controls.php');}
if($_POST['view'] == "Services"){include('scripts/service_controls.php');}
if($_POST['view'] == "Spectrogram"){include('spectrogram.php');}
if($_POST['view'] == "Overview"){include('overview.php');}
if($_POST['view'] == "Today's Detections"){include('todays_detections.php');}
if($_POST['view'] == "Best Recordings"){echo "<br><br>";include('stats.php');}
if($_POST['view'] == "Species Stats"){header('location:/stats');}
if($_POST['view'] == "Daily Charts"){include('history.php');}
if($_POST['view'] == "Tools"){
if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/thisrun.txt');
} elseif (file_exists('/home/pi/BirdNET-Pi/firstrun.ini')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/firstrun.ini');
}
$caddypwd = $config['CADDY_PWD'];
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You cannot edit the settings for this installation';
exit;
} else {
$submittedpwd = $_SERVER['PHP_AUTH_PW'];
$submitteduser = $_SERVER['PHP_AUTH_USER'];
if($submittedpwd == $caddypwd && $submitteduser == 'birdnet'){
$url = $_SERVER['SERVER_NAME']."/scripts/adminer.php";
echo "<div class=\"centered\">
<form action=\"\" method=\"POST\" id=\"views\">
<button type=\"submit\" name=\"view\" value=\"Settings\" form=\"views\">Settings</button>
<button type=\"submit\" name=\"view\" value=\"System Info\" form=\"views\">System Info</button>
<button type=\"submit\" name=\"view\" value=\"System Controls\" form=\"views\">System Controls</button>
<button type=\"submit\" name=\"view\" value=\"Services\" form=\"views\">Services</button>
<button type=\"submit\" name=\"view\" value=\"File\" form=\"views\">File Manager</button>
<a href=\"scripts/adminer.php\" target=\"_top\"><button type=\"submit\" form=\"\">Database Maintenanace</button></a>
<button type=\"submit\" name=\"view\" value=\"Webterm\" form=\"views\">Web Terminal</button>
<button type=\"submit\" name=\"view\" value=\"Included\" form=\"views\">Custom Species List</button>
<button type=\"submit\" name=\"view\" value=\"Excluded\" form=\"views\">Excluded Species List</button>
</form>
</div>";
} else {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You cannot edit the settings for this installation';
exit;
}
}
}
if($_POST['view'] == "Recordings"){include('play.php');}
if($_POST['view'] == "Settings"){include('scripts/config.php');}
if($_POST['view'] == "Advanced"){include('scripts/advanced.php');}
if($_POST['view'] == "Included"){
if(isset($_POST['species']) && isset($_POST['add'])){
$file = '/home/pi/BirdNET-Pi/include_species_list.txt';
$str = file_get_contents("$file");
$str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str);
file_put_contents("$file", "$str");
if(isset($_POST['species'])){
foreach ($_POST['species'] as $selectedOption)
file_put_contents("/home/pi/BirdNET-Pi/include_species_list.txt", $selectedOption."\n", FILE_APPEND);
}
} elseif(isset($_POST['species']) && isset($_POST['del'])){
$file = '/home/pi/BirdNET-Pi/include_species_list.txt';
$str = file_get_contents("$file");
$str = preg_replace('/^\h*\v+/m', '', $str);
file_put_contents("$file", "$str");
foreach($_POST['species'] as $selectedOption) {
$content = file_get_contents("/home/pi/BirdNET-Pi/include_species_list.txt");
$newcontent = str_replace($selectedOption, "", "$content");
file_put_contents("/home/pi/BirdNET-Pi/include_species_list.txt", "$newcontent");
}
$file = '/home/pi/BirdNET-Pi/include_species_list.txt';
$str = file_get_contents("$file");
$str = preg_replace('/^\h*\v+/m', '', $str);
file_put_contents("$file", "$str");
}
include('scripts/include_list.php');
}
if($_POST['view'] == "Excluded"){
if(isset($_POST['species']) && isset($_POST['add'])){
$file = '/home/pi/BirdNET-Pi/exclude_species_list.txt';
$str = file_get_contents("$file");
$str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str);
file_put_contents("$file", "$str");
foreach ($_POST['species'] as $selectedOption)
file_put_contents("/home/pi/BirdNET-Pi/exclude_species_list.txt", $selectedOption."\n", FILE_APPEND);
} elseif (isset($_POST['species']) && isset($_POST['del'])){
$file = '/home/pi/BirdNET-Pi/exclude_species_list.txt';
$str = file_get_contents("$file");
$str = preg_replace('/^\h*\v+/m', '', $str);
file_put_contents("$file", "$str");
foreach($_POST['species'] as $selectedOption) {
$content = file_get_contents("/home/pi/BirdNET-Pi/exclude_species_list.txt");
$newcontent = str_replace($selectedOption, "", "$content");
file_put_contents("/home/pi/BirdNET-Pi/exclude_species_list.txt", "$newcontent");
}
$file = '/home/pi/BirdNET-Pi/exclude_species_list.txt';
$str = file_get_contents("$file");
$str = preg_replace('/^\h*\v+/m', '', $str);
file_put_contents("$file", "$str");
}
include('scripts/exclude_list.php');
}
if($_POST['view'] == "File"){
header('Location: scripts/filemanager/filemanager.php');
}
if($_POST['view'] == "Webterm"){
if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/thisrun.txt');
} elseif (file_exists('/home/pi/BirdNET-Pi/firstrun.ini')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/firstrun.ini');
}
$caddypwd = $config['CADDY_PWD'];
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You cannot access the web terminal';
exit;
} else {
$submittedpwd = $_SERVER['PHP_AUTH_PW'];
$submitteduser = $_SERVER['PHP_AUTH_USER'];
if($submittedpwd == $caddypwd && $submitteduser == 'birdnet'){
#ACCESS THE WEB TERMINAL
header("Location: /terminal");
} else {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You cannot access the web terminal';
exit;
}
}
}
} elseif(isset($_POST['submit'])) {
$command = $_POST['submit'];
if(isset($command)){
$results = shell_exec("$command 2>&1");
echo "<pre>$results</pre>";
}
ob_end_flush();
} else {include('overview.php');}
?>
<script>
function myFunction() {
var x = document.getElementById("myTopnav");
if (x.className === "topnav") {
x.className += " responsive";
} else {
x.className = "topnav";
}
}
</script>
</div>
</body>
+9 -1
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
# Simple new installer
HOME=/home/pi
USER=pi
branch=main
@@ -8,5 +9,12 @@ if ! which git &> /dev/null;then
sudo apt -y install git
fi
git clone -b ${branch} https://github.com/mcguirepr89/BirdNET-Pi.git ${HOME}/BirdNET-Pi &&
cp ${HOME}/BirdNET-Pi/birdnet.conf-defaults ${HOME}/BirdNET-Pi/birdnet.conf &&
${HOME}/BirdNET-Pi/scripts/install_birdnet.sh
if [ ${PIPESTATUS[0]} -eq 0 ];then
echo "Installation completed successfully"
sudo reboot
else
echo "The installation exited unsuccessfully."
exit 1
fi
+6 -1
View File
@@ -1,8 +1,13 @@
pip
wheel
setuptools
tflite_runtime-2.6.0-cp39-none-linux_aarch64.whl
colorama==0.4.4
librosa
mysql-connector-python
pytz
tzlocal
pandas
seaborn
babel
streamlit
plotly
-14
View File
@@ -1,14 +0,0 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
$file='/home/pi/BirdNET-Pi/exclude_species_list.txt';
$str=file_get_contents("$file");
$str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str);
file_put_contents("$file", "$str");
if (isset($_POST['species']))
foreach ($_POST['species'] as $selectedOption)
file_put_contents("/home/pi/BirdNET-Pi/exclude_species_list.txt", $selectedOption."\n", FILE_APPEND);
header("Location: {$_SERVER['HTTP_REFERER']}");
exit;
?>
-14
View File
@@ -1,14 +0,0 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
$file='/home/pi/BirdNET-Pi/include_species_list.txt';
$str=file_get_contents("$file");
$str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str);
file_put_contents("$file", "$str");
if (isset($_POST['species']))
foreach ($_POST['species'] as $selectedOption)
file_put_contents("/home/pi/BirdNET-Pi/include_species_list.txt", $selectedOption."\n", FILE_APPEND);
header("Location: {$_SERVER['HTTP_REFERER']}");
exit;
?>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+659 -1546
View File
File diff suppressed because one or more lines are too long
+37 -166
View File
@@ -7,6 +7,23 @@ if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
} elseif (file_exists('/home/pi/BirdNET-Pi/firstrun.ini')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/firstrun.ini');
}
$caddypwd = $config['CADDY_PWD'];
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You cannot edit the settings for this installation';
exit;
} else {
$submittedpwd = $_SERVER['PHP_AUTH_PW'];
$submitteduser = $_SERVER['PHP_AUTH_USER'];
if($submittedpwd !== $caddypwd || $submitteduser !== 'birdnet'){
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You cannot edit the settings for this installation';
exit;
}
}
if(isset($_POST['submit'])) {
$contents = file_get_contents("/home/pi/BirdNET-Pi/birdnet.conf");
@@ -25,15 +42,6 @@ if(isset($_POST['submit'])) {
}
}
if(isset($_POST["db_pwd"])) {
$db_pwd = $_POST["db_pwd"];
if(strcmp($db_pwd,$config['DB_PWD']) !== 0) {
shell_exec('sudo /usr/local/bin/update_db_pwd_bullseye.sh');
$contents = preg_replace("/DB_PWD=.*/", "DB_PWD=$db_pwd", $contents);
$contents2 = preg_replace("/DB_PWD=.*/", "DB_PWD=$db_pwd", $contents2);
}
}
if(isset($_POST["ice_pwd"])) {
$ice_pwd = $_POST["ice_pwd"];
if(strcmp($ice_pwd,$config['ICE_PWD']) !== 0) {
@@ -42,30 +50,6 @@ if(isset($_POST['submit'])) {
}
}
if(isset($_POST["webterminal_url"])) {
$webterminal_url = $_POST["webterminal_url"];
if(strcmp($webterminal_url,$config['WEBTERMINAL_URL']) !== 0) {
$contents = preg_replace("/WEBTERMINAL_URL=.*/", "WEBTERMINAL_URL=$webterminal_url", $contents);
$contents2 = preg_replace("/WEBTERMINAL_URL=.*/", "WEBTERMINAL_URL=$webterminal_url", $contents2);
$fh = fopen("/home/pi/BirdNET-Pi/birdnet.conf", "w");
$fh2 = fopen("/home/pi/BirdNET-Pi/thisrun.txt", "w");
fwrite($fh, $contents);
fwrite($fh2, $contents2);
}
}
if(isset($_POST["birdnetlog_url"])) {
$birdnetlog_url = $_POST["birdnetlog_url"];
if(strcmp($birdnetlog_url,$config['BIRDNETLOG_URL']) !== 0) {
$contents = preg_replace("/BIRDNETLOG_URL=.*/", "BIRDNETLOG_URL=$birdnetlog_url", $contents);
$contents2 = preg_replace("/BIRDNETLOG_URL=.*/", "BIRDNETLOG_URL=$birdnetlog_url", $contents2);
$fh = fopen("/home/pi/BirdNET-Pi/birdnet.conf", "w");
$fh2 = fopen("/home/pi/BirdNET-Pi/thisrun.txt", "w");
fwrite($fh, $contents);
fwrite($fh2, $contents2);
}
}
if(isset($_POST["birdnetpi_url"])) {
$birdnetpi_url = $_POST["birdnetpi_url"];
if(strcmp($birdnetpi_url,$config['BIRDNETPI_URL']) !== 0) {
@@ -155,104 +139,13 @@ if(isset($_POST['submit'])) {
$fh2 = fopen("/home/pi/BirdNET-Pi/thisrun.txt", "w");
fwrite($fh, $contents);
fwrite($fh2, $contents2);
@session_start();
if(true){
$_SESSION['success'] = 1;
}
}
?>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
* {
font-family: 'Arial', 'Gill Sans', 'Gill Sans MT',
' Calibri', 'Trebuchet MS', 'sans-serif';
box-sizing: border-box;
}
/* Create two unequal columns that floats next to each other */
.column {
float: left;
padding: 10px;
}
.first {
width: calc(50% - 70px);
}
.second {
width: calc(50% - 70px);
}
.
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
body {
background-color: rgb(119, 196, 135);
}
a {
font-size:large;
text-decoration: none;
}
.block {
display: block;
width:50%;
border: none;
padding: 10px 10px;
font-size: medium;
cursor: pointer;
text-align: center;
}
form {
text-align:left;
margin-left:20px;
}
h2 {
margin-bottom:0px;
}
h3 {
margin-left: -10px;
text-align:left;
}
label {
float:left;
width: 40%;
font-weight:bold;
}
input,select {
width: 60%;
text-align:center;
font-size:large;
}
@media screen and (max-width: 1000px) {
h2,h3 {
text-align:center;
}
form {
margin:0;
}
.column {
float: none;
width: 100%;
}
input, label {
width: 100%;
{
}
</style>
</head>
<div class="settings">
<?php
if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
$newconfig = parse_ini_file('/home/pi/BirdNET-Pi/thisrun.txt');
@@ -261,20 +154,17 @@ if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
}
?>
<h2>Advanced Settings</h2>
<body style="background-color: rgb(119, 196, 135);">
<div class="row">
<div class="column first">
<form action="advanced.php" method="POST">
<form action="" method="POST">
<h3>Defaults</h3>
<label>Full Disk Behavior: </label>
<label style="width:30%;" for="purge">
<input style="width:15%;" name="full_disk" type="radio" id="purge" value="purge"
<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 style="width:30%;" for="keep">
<input style="width:15%" name="full_disk" type="radio" id="keep" value="keep"
<label for="keep">
<input name="full_disk" type="radio" id="keep" value="keep"
<?php
if (strcmp($newconfig['FULL_DISK'], "keep") == 0) {
echo "checked";
@@ -288,7 +178,7 @@ if (strcmp($newconfig['FULL_DISK'], "purge") == 0) {
<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" 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>
<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" type="number" min="3" max="<?php print($newconfig['RECORDING_LENGTH']);?>" value="<?php print($newconfig['EXTRACTION_LENGTH']);?>" /><br>
<p>Set Extraction Length to something less than your Recording Length. Min=3 Max=Recording Length</p>
@@ -302,28 +192,14 @@ foreach($formats as $format){
}
?>
</select>
<h3>Passwords</h3>
<h3>BirdNET-Pi Password</h3>
<label for="caddy_pwd">Webpage: </label>
<input name="caddy_pwd" type="text" value="<?php print($newconfig['CADDY_PWD']);?>" /><br>
<p>This password protects the Live Audio Stream, the Processed extractions, phpSysInfo, your Tools, and WebTerminal. When you update this value, the web server will reload, so wait about 30 seconds and then reload the page.</p>
<label for="db_pwd">Database: </label>
<input name="db_pwd" type="text" value="<?php print($newconfig['DB_PWD']);?>" required/><br>
<p>This password protects the database. When you update this value, it will be updated automatically.</p>
<label for="ice_pwd">Live Audio Stream: </label>
<input name="ice_pwd" type="text" value="<?php print($newconfig['ICE_PWD']);?>" required/><br>
</div>
<div class="column second">
<h3>Custom URLs</h3>
<p>When you update any of the URL settings below, the web server will reload, so be sure to wait at least 30 seconds and then reload the page.</p>
<h3>Custom URL</h3>
<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 name="birdnetpi_url" type="url" value="<?php print($newconfig['BIRDNETPI_URL']);?>" /><br>
<p>This 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 `http://`.<br>Example for IP:http://192.168.0.109<br>Example if you own your own domain:https://birdnetpi.pmcgui.xyz</p>
<label for="birdnetlog_url">BirdNET-Lite Log URL: </label>
<input name="birdnetlog_url" type="url" value="<?php print($newconfig['BIRDNETLOG_URL']);?>" /><br>
<p>This URL is how the log will be reached. Only use this variable if you own your own domain.</p>
<label for="webterminal_url">Web Terminal URL: </label>
<input name="webterminal_url" type="url" value="<?php print($newconfig['WEBTERMINAL_URL']);?>" /><br>
<p>This URL is how the Web browser terminal will be reached. Only use this variable if you own your own domain.</p>
<h3>BirdNET-Lite Settings</h3>
<label for="overlap">Overlap: </label>
<input name="overlap" type="number" min="0.0" max="2.9" step="0.1" value="<?php print($newconfig['OVERLAP']);?>" required/><br>
@@ -335,24 +211,19 @@ 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>
<p>Min=0.5, Max=1.5</p>
<br><br>
<button type="submit" name="submit" class="block"><?php
if(isset($_SESSION['success'])){
<input type="hidden" name="view" value="Advanced">
<button type="submit" name="submit" value="advanced">
<?php
if(isset($_POST['submit'])){
echo "Success!";
unset($_SESSION['success']);
} else {
echo "Update Settings";
}
?></button>
?>
</button>
<br>
</form>
<form action="config.php" style="margin:0;">
<button type="submit" class="block">Basic Settings</button>
</form>
<br>
<form action="index.html" style="margin:0;">
<button type="submit" class="block">Tools</button>
</form>
</form>
<form action="" method="POST">
<button type="submit" name="view" value="Settings">Basic Settings</button>
</form>
</div>
</div>
</body>
Regular → Executable
+1
View File
@@ -1,3 +1,4 @@
#!/home/pi/BirdNET-Pi/birdnet/bin/python3
import argparse
import socket
-12
View File
@@ -1,12 +0,0 @@
<html lang="en">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head></head>
<body style="background-color: rgb(119, 196, 135);font-size:large;">
A backup is being made that includes all of<br>
your extractions and the BirdDB.txt<br>
<br></br>
The backup is in /home/pi/BirdNET-Pi
</body>
</html>
-997
View File
@@ -1,997 +0,0 @@
#!/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
+26 -27
View File
@@ -95,9 +95,8 @@ run_analysis() {
WEEK="$(echo "${WEEK_OF_YEAR} + 4" |bc -l)"
fi
cd ${HOME}/BirdNET-Pi/scripts || exit 1
for i in "${files[@]}";do
echo "${1}/${i}" > ../analyzing_now.txt
echo "${1}/${i}" > /home/pi/BirdNET-Pi/analyzing_now.txt
[ -z ${RECORDING_LENGTH} ] && RECORDING_LENGTH=15
[ ${RECORDING_LENGTH} == "60" ] && RECORDING_LENGTH=01:00
FILE_LENGTH="$(ffmpeg -i ${1}/${i} 2>&1 | awk -F. '/Duration/ {print $1}' | cut -d':' -f3-4)"
@@ -131,7 +130,7 @@ run_analysis() {
done
fi
if [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then
echo "python3 analyze.py \
echo "analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
@@ -140,7 +139,7 @@ run_analysis() {
--overlap "${OVERLAP}" \
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}""
"${VENV}"/bin/python analyze.py \
analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
@@ -150,7 +149,7 @@ run_analysis() {
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}"
elif [ -f ${1}/${i} ] && [ -f ${INCLUDELIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then
echo "python3 analyze.py \
echo "analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
@@ -160,18 +159,18 @@ run_analysis() {
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}" \
--include_list "${INCLUDE_LIST}""
"${VENV}"/bin/python analyze.py \
analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
--lon "${LONGITUDE}" \
--week "${WEEK}" \
--overlap "${OVERLAP}" \
--sensitivity "${SENSITIVITY}" \
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}" \
--include_list "${INCLUDE_LIST}"
--include_list "${INCLUDE_LIST}"
elif [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then
echo "python3 analyze.py \
echo "analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
@@ -181,18 +180,18 @@ run_analysis() {
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}" \
--exclude_list "${EXCLUDE_LIST}""
"${VENV}"/bin/python analyze.py \
analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
--lon "${LONGITUDE}" \
--week "${WEEK}" \
--overlap "${OVERLAP}" \
--sensitivity "${SENSITIVITY}" \
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}" \
--exclude_list "${EXCLUDE_LIST}"
--exclude_list "${EXCLUDE_LIST}"
elif [ -f ${1}/${i} ] && [ -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ -z $BIRDWEATHER_ID ];then
echo "python3 analyze.py \
echo "analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
@@ -203,19 +202,19 @@ run_analysis() {
--min_conf "${CONFIDENCE}" \
--include_list "${INCLUDE_LIST}" \
--exclude_list "${EXCLUDE_LIST}""
"${VENV}"/bin/python analyze.py \
analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
--lon "${LONGITUDE}" \
--week "${WEEK}" \
--overlap "${OVERLAP}" \
--sensitivity "${SENSITIVITY}" \
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}" \
--include_list "${INCLUDE_LIST}" \
--exclude_list "${EXCLUDE_LIST}"
elif [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then
echo "python3 analyze.py \
echo "analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
@@ -225,18 +224,18 @@ run_analysis() {
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}" \
--birdweather_id "IN_USE""
"${VENV}"/bin/python analyze.py \
analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
--lon "${LONGITUDE}" \
--week "${WEEK}" \
--overlap "${OVERLAP}" \
--sensitivity "${SENSITIVITY}" \
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}" \
--birdweather_id "${BIRDWEATHER_ID}"
elif [ -f ${1}/${i} ] && [ -f ${INCLUDE_LIST} ] && [ ! -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then
echo "python3 analyze.py \
echo "analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
@@ -247,19 +246,19 @@ run_analysis() {
--min_conf "${CONFIDENCE}" \
--include_list "${INCLUDE_LIST}" \
--birdweather_id "IN_USE""
"${VENV}"/bin/python analyze.py \
analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
--lon "${LONGITUDE}" \
--week "${WEEK}" \
--overlap "${OVERLAP}" \
--sensitivity "${SENSITIVITY}" \
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}" \
--include_list "${INCLUDE_LIST}" \
--birdweather_id "${BIRDWEATHER_ID}"
elif [ -f ${1}/${i} ] && [ ! -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then
echo "python3 analyze.py \
echo "analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
@@ -270,19 +269,19 @@ run_analysis() {
--min_conf "${CONFIDENCE}" \
--exclude_list "${EXCLUDE_LIST}" \
--birdweather_id "IN_USE""
"${VENV}"/bin/python analyze.py \
analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
--lon "${LONGITUDE}" \
--week "${WEEK}" \
--overlap "${OVERLAP}" \
--sensitivity "${SENSITIVITY}" \
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}" \
--exclude_list "${EXCLUDE_LIST}" \
--birdweather_id "${BIRDWEATHER_ID}"
elif [ -f ${1}/${i} ] && [ -f ${INCLUDE_LIST} ] && [ -f ${EXCLUDE_LIST} ] && [ ! -z $BIRDWEATHER_ID ];then
echo "python3 analyze.py \
echo "analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
@@ -294,14 +293,14 @@ run_analysis() {
--include_list "${INCLUDE_LIST}" \
--exclude_list "${EXCLUDE_LIST}" \
--birdweather_id "IN_USE""
"${VENV}"/bin/python analyze.py \
analyze.py \
--i "${1}/${i}" \
--o "${1}/${i}.csv" \
--lat "${LATITUDE}" \
--lon "${LONGITUDE}" \
--week "${WEEK}" \
--overlap "${OVERLAP}" \
--sensitivity "${SENSITIVITY}" \
--sensitivity "${SENSITIVITY}" \
--min_conf "${CONFIDENCE}" \
--include_list "${INCLUDE_LIST}" \
--exclude_list "${EXCLUDE_LIST}" \
+2
View File
@@ -10,6 +10,8 @@ fi
[ -z $RECORDING_LENGTH ] && RECORDING_LENGTH=15
if ! pulseaudio --check;then pulseaudio --start;fi
if pgrep arecord &> /dev/null ;then
echo "Recording"
else
+1 -1
View File
@@ -4,7 +4,7 @@ if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
} elseif (file_exists('/home/pi/BirdNET-Pi/firstrun.ini')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/firstrun.ini');
}
$template = file_get_contents("email_template");
$template = file_get_contents("scripts/email_template");
foreach($config as $key => $value)
{
+30 -70
View File
@@ -9,82 +9,42 @@ USER=pi
my_dir=${HOME}/BirdNET-Pi/scripts
echo "Stopping services"
sudo systemctl stop birdnet_recording.service
sudo systemctl stop birdnet_analysis.service
sudo systemctl stop birdnet_server.service
echo "Removing all data . . . "
sudo rm -drf "${RECS_DIR}"
sudo rm -f "${IDFILE}"
sudo rm -f $(dirname ${my_dir})/BirdDB.txt
echo "Creating necessary directories"
[ -d ${EXTRACTED} ] || sudo -u ${USER} mkdir -p ${EXTRACTED}
[ -d ${EXTRACTED}/By_Date ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/By_Date
[ -d ${EXTRACTED}/By_Common_Name ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/By_Common_Name
[ -d ${EXTRACTED}/By_Scientific_Name ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/By_Scientific_Name
[ -d ${EXTRACTED}/Charts ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/Charts
[ -d ${PROCESSED} ] || sudo -u ${USER} mkdir -p ${PROCESSED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/homepage/* ${EXTRACTED}
if [ ! -z ${BIRDNETLOG_URL} ];then
BIRDNETLOG_URL="$(echo ${BIRDNETLOG_URL} | sed 's/\/\//\\\/\\\//g')"
sudo -u${USER} sed -i "s/http:\/\/$(hostname).local:8080/"${BIRDNETLOG_URL}"/g" $(dirname ${my_dir})/homepage/*.html
phpfiles="$(grep -l "$(hostname).local:8080" ${my_dir}/*.php)"
for i in "${phpfiles[@]}";do
sudo -u${USER} sed -i "s/http:\/\/$(hostname).local:8080/"${BIRDNETLOG_URL}"/g" ${i}
done
fi
if [ ! -z ${WEBTERMINAL_URL} ];then
WEBTERMINAL_URL="$(echo ${WEBTERMINAL_URL} | sed 's/\/\//\\\/\\\//g')"
sudo -u${USER} sed -i "s/http:\/\/$(hostname).local:8888/"${WEBTERMINAL_URL}"/g" $(dirname ${my_dir})/homepage/*.html
phpfiles="$(grep -l "$(hostname).local:8888" ${my_dir}/*.php)"
for i in "${phpfiles[@]}";do
sudo -u${USER} sed -i "s/http:\/\/$(hostname).local:8888/"${WEBTERMINAL_URL}"/g" ${i}
done
fi
sudo -u ${USER} ln -fs $(dirname ${my_dir})/model/labels.txt ${my_dir}/
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts ${EXTRACTED}
if [ -z ${BIRDNETPI_URL} ];then
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*/*.php
else
BIRDNETPI_URL="$(echo ${BIRDNETPI_URL} | sed 's/\/\//\\\/\\\//g')"
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*/*.php
fi
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/spectrogram.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/viewday.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/overview.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/stats.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/viewdb.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/history.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/homepage/images/favicon.ico ${EXTRACTED}
sudo -u ${USER} ln -fs ${HOME}/phpsysinfo ${EXTRACTED}
sudo -u ${USER} cp -f $(dirname ${my_dir})/templates/phpsysinfo.ini ${HOME}/phpsysinfo/
sudo -u ${USER} cp -f $(dirname ${my_dir})/templates/green_bootstrap.css ${HOME}/phpsysinfo/templates/
sudo -u ${USER} cp -f $(dirname ${my_dir})/templates/index_bootstrap.html ${HOME}/phpsysinfo/templates/html
echo "Setting Wttr.in URL to "${LATITUDE}", "${LONGITUDE}""
sudo -u${USER} sed -i "s/https:\/\/v2.wttr.in\//https:\/\/v2.wttr.in\/"${LATITUDE},${LONGITUDE}"/g" $(dirname ${my_dir})/homepage/menu.html
chmod -R g+rw $(dirname ${my_dir})
chmod -R g+rw ${RECS_DIR}
echo "Generating BirdDB.txt"
if ! [ -f $(dirname ${my_dir})/BirdDB.txt ];then
sudo -u ${USER} touch $(dirname ${my_dir})/BirdDB.txt
echo "Date;Time;Sci_Name;Com_Name;Confidence;Lat;Lon;Cutoff;Week;Sens;Overlap" | sudo -u ${USER} tee -a $(dirname ${my_dir})/BirdDB.txt
elif ! grep Date $(dirname ${my_dir})/BirdDB.txt;then
sudo -u ${USER} sed -i '1 i\Date;Time;Sci_Name;Com_Name;Confidence;Lat;Lon;Cutoff;Week;Sens;Overlap' $(dirname ${my_dir})/BirdDB.txt
fi
ln -sf $(dirname ${my_dir})/BirdDB.txt ${my_dir}/BirdDB.txt &&
chown pi:pi ${my_dir}/BirdDB.txt && chmod g+rw ${my_dir}/BirdDB.txt
[ -d ${EXTRACTED} ] || mkdir -p ${EXTRACTED}
[ -d ${EXTRACTED}/By_Date ] || mkdir -p ${EXTRACTED}/By_Date
[ -d ${EXTRACTED}/Charts ] || mkdir -p ${EXTRACTED}/Charts
[ -d ${PROCESSED} ] || mkdir -p ${PROCESSED}
ln -fs $(dirname ${my_dir})/homepage/* ${EXTRACTED}
ln -fs $(dirname ${my_dir})/model/labels.txt ${my_dir}/
ln -fs $(dirname ${my_dir})/scripts ${EXTRACTED}
ln -fs $(dirname ${my_dir})/scripts/play.php ${EXTRACTED}
ln -fs $(dirname ${my_dir})/scripts/spectrogram.php ${EXTRACTED}
ln -fs $(dirname ${my_dir})/scripts/overview.php ${EXTRACTED}
ln -fs $(dirname ${my_dir})/scripts/stats.php ${EXTRACTED}
ln -fs $(dirname ${my_dir})/scripts/viewdb.php ${EXTRACTED}
ln -fs $(dirname ${my_dir})/scripts/history.php ${EXTRACTED}
ln -fs $(dirname ${my_dir})/homepage/images/favicon.ico ${EXTRACTED}
ln -fs ${HOME}/phpsysinfo ${EXTRACTED}
ln -fs $(dirname ${my_dir})/templates/phpsysinfo.ini ${HOME}/phpsysinfo/
ln -fs $(dirname ${my_dir})/templates/green_bootstrap.css ${HOME}/phpsysinfo/templates/
ln -fs $(dirname ${my_dir})/templates/index_bootstrap.html ${HOME}/phpsysinfo/templates/html
sudo chmod -R g+rw $(dirname ${my_dir})
sudo chmod -R g+rw ${EXTRACTED}
echo "Dropping and re-creating database"
sudo /home/pi/BirdNET-Pi/scripts/createdb_bullseye.sh
createdb.sh
echo "Generating BirdDB.txt"
touch $(dirname ${my_dir})/BirdDB.txt
echo "Date;Time;Sci_Name;Com_Name;Confidence;Lat;Lon;Cutoff;Week;Sens;Overlap" > $(dirname ${my_dir})/BirdDB.txt
ln -sf $(dirname ${my_dir})/BirdDB.txt ${my_dir}/BirdDB.txt
chown pi:pi ${my_dir}/BirdDB.txt && chmod g+rw ${my_dir}/BirdDB.txt
echo "Restarting services"
sudo systemctl start birdnet_recording.service
restart_services.sh
+66 -122
View File
@@ -1,125 +1,72 @@
<?php
session_start();
error_reporting(E_ALL);
ini_set('display_errors',1);
# Basic Settings
if(isset($_POST["latitude"])){
$latitude = $_POST["latitude"];
$longitude = $_POST["longitude"];
$birdweather_id = $_POST["birdweather_id"];
$pushed_app_key = $_POST["pushed_app_key"];
$pushed_app_secret = $_POST["pushed_app_secret"];
$contents = file_get_contents("/home/pi/BirdNET-Pi/birdnet.conf");
$contents = preg_replace("/LATITUDE=.*/", "LATITUDE=$latitude", $contents);
$contents = preg_replace("/LONGITUDE=.*/", "LONGITUDE=$longitude", $contents);
$contents = preg_replace("/BIRDWEATHER_ID=.*/", "BIRDWEATHER_ID=$birdweather_id", $contents);
$contents = preg_replace("/PUSHED_APP_KEY=.*/", "PUSHED_APP_KEY=$pushed_app_key", $contents);
$contents = preg_replace("/PUSHED_APP_SECRET=.*/", "PUSHED_APP_SECRET=$pushed_app_secret", $contents);
$contents2 = file_get_contents("/home/pi/BirdNET-Pi/thisrun.txt");
$contents2 = preg_replace("/LATITUDE=.*/", "LATITUDE=$latitude", $contents2);
$contents2 = preg_replace("/LONGITUDE=.*/", "LONGITUDE=$longitude", $contents2);
$contents2 = preg_replace("/BIRDWEATHER_ID=.*/", "BIRDWEATHER_ID=$birdweather_id", $contents2);
$contents2 = preg_replace("/PUSHED_APP_KEY=.*/", "PUSHED_APP_KEY=$pushed_app_key", $contents2);
$contents2 = preg_replace("/PUSHED_APP_SECRET=.*/", "PUSHED_APP_SECRET=$pushed_app_secret", $contents2);
$fh = fopen("/home/pi/BirdNET-Pi/birdnet.conf", "w");
$fh2 = fopen("/home/pi/BirdNET-Pi/thisrun.txt", "w");
fwrite($fh, $contents);
fwrite($fh2, $contents2);
$language = $_POST["language"];
if ($language != "none"){
$command = "sudo -upi mv /home/pi/BirdNET-Pi/model/labels.txt /home/pi/BirdNET-Pi/model/labels.txt.old && sudo -upi unzip /home/pi/BirdNET-Pi/model/labels_l18n.zip $language -d /home/pi/BirdNET-Pi/model && sudo -upi mv /home/pi/BirdNET-Pi/model/$language /home/pi/BirdNET-Pi/model/labels.txt";
$command_output = `$command`;
}
}
?>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
* {
font-family: 'Arial', 'Gill Sans', 'Gill Sans MT',
' Calibri', 'Trebuchet MS', 'sans-serif';
box-sizing: border-box;
}
/* Create two unequal columns that floats next to each other */
.column {
float: left;
padding: 10px;
}
.first {
width: calc(50% - 70px);
}
.second {
width: calc(50% - 30px);
}
.
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
body {
background-color: rgb(119, 196, 135);
}
a {
text-decoration: none;
}
.block {
display: block;
width:50%;
border: none;
padding: 10px 10px;
font-size: medium;
cursor: pointer;
text-align: center;
}
select {
font-size:large;
width: 60%;
}
select option {
font-size:large;
}
form {
text-align:left;
margin-left:20px;
}
h2 {
margin-bottom:0px;
}
h3 {
margin-left: -10px;
text-align:left;
}
label {
float:left;
width:40%;
font-weight:bold;
}
input {
width:60%;
text-align:center;
font-size:large;
}
@media screen and (max-width: 800px) {
select {
width:100%;
}
h2 {
margin-bottom:0px;
text-align:center;
}
form {
text-align:left;
margin-left:0px;
}
.column {
float: none;
width: 100%;
}
input, label {
width:100%;
{
}
</style>
</head>
<div class="settings">
<h2>Basic Settings</h2>
<body style="background-color: rgb(119, 196, 135);">
<div class="row">
<div class="column first">
<form action="write_config.php" method="POST" name="normal">
<form action="" method="POST">
<?php
if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/thisrun.txt');
} elseif (file_exists('/home/pi/BirdNET-Pi/firstrun.ini')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/firstrun.ini');
} ?>
}
$caddypwd = $config['CADDY_PWD'];
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You cannot edit the settings for this installation';
exit;
} else {
$submittedpwd = $_SERVER['PHP_AUTH_PW'];
$submitteduser = $_SERVER['PHP_AUTH_USER'];
if($submittedpwd !== $caddypwd || $submitteduser !== 'birdnet'){
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You cannot edit the settings for this installation';
exit;
}
}
?>
<label for="latitude">Latitude: </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>
@@ -167,22 +114,19 @@ if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
<option value="labels_uk.txt">Ukrainian</option>
</select>
<br><br>
<button type="submit" name="normal" class="block"><?php
if(isset($_SESSION['success'])){
<input type="hidden" name="status" value="success">
<input type="hidden" name="submit" value="settings">
<button type="submit" name="view" value="Settings">
<?php
if(isset($_POST['status'])){
echo "Success!";
unset($_SESSION['success']);
} else {
echo "Update Settings";
}
?></button>
</form>
<form action="advanced.php" class="form2">
<button type="submit" class="block">Advanced Settings</button>
</form>
<form action="index.html" class="form2">
<button type="submit" class="block">Tools</button>
</form>
</div>
</div>
</body>
?>
</button>
</form>
<form action="" method="POST">
<button type="submit" name="view" value="Advanced">Advanced Settings</button>
</form>
</div>
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
source /etc/birdnet/birdnet.conf
sqlite3 /home/pi/BirdNET-Pi/scripts/birds.db << EOF
DROP TABLE IF EXISTS detections;
CREATE TABLE IF NOT EXISTS detections (
Date DATE,
Time TIME,
Sci_Name VARCHAR(100) NOT NULL,
Com_Name VARCHAR(100) NOT NULL,
Confidence FLOAT,
Lat FLOAT,
Lon FLOAT,
Cutoff FLOAT,
Week INT,
Sens FLOAT,
Overlap FLOAT,
File_Name VARCHAR(100) NOT NULL);
EOF
sudo chown pi:pi /home/pi/BirdNET-Pi/scripts/birds.db
sudo chmod g+w /home/pi/BirdNET-Pi/scripts/birds.db
-46
View File
@@ -1,46 +0,0 @@
#!/usr/bin/env bash
# This script performs the mysql_secure_installation
# Creates the birds database
# Creates the detections table
# Creates the birder user and grants them appropriate
# permissions
# If using this script to re-initialize (DROP then CREATE)
# the DB, be sure to run this as root or with sudo
source /etc/birdnet/birdnet.conf
mysql_secure_installation << EOF
y
n
y
y
y
y
EOF
mysql << EOF
DROP DATABASE IF EXISTS birds;
CREATE DATABASE IF NOT EXISTS birds;
USE birds;
CREATE TABLE IF NOT EXISTS detections (
Date DATE,
Time TIME,
Sci_Name VARCHAR(100) NOT NULL,
Com_Name VARCHAR(100) NOT NULL,
Confidence FLOAT,
Lat FLOAT,
Lon FLOAT,
Cutoff FLOAT,
Week INT,
Sens FLOAT,
Overlap FLOAT);
GRANT ALL ON birds.* TO 'birder'@'localhost' IDENTIFIED BY '${DB_PWD}' WITH GRANT OPTION;
FLUSH PRIVILEGES;
exit
EOF
sudo -u${USER} sed -i "s/databasepassword/${DB_PWD}/g" /home/pi/BirdNET-Pi/scripts/server.py
sed -i "s/mysqli.default_host =.*/mysqli.default_host = localhost/g" /etc/php/7.4/fpm/php.ini
sed -i "s/mysqli.default_user =.*/mysqli.default_user = birder/g" /etc/php/7.4/fpm/php.ini
sed -i "s/mysqli.default_pw =.*/mysqli.default_pw = ${DB_PWD}/g" /etc/php/7.4/fpm/php.ini
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -x
source /etc/birdnet/birdnet.conf
STAMP="%H:%M:%S"
[ -z $RECORDING_LENGTH ] && RECORDING_LENGTH=15
if ! pulseaudio --check;then pulseaudio --start;fi
CUSTOM_WINDOW_START=(0 3 15 22)
CUSTOM_WINDOW_END=(0 7 19 23)
RECORDING_DURATION=60
PAUSE_DURATION=240
now=$(date +%H)
while [ $now -ge ${CUSTOM_WINDOW_START[0]} ] && [ $now -le ${CUSTOM_WINDOW_END[0]} ];do
echo $now
# If you prefer no directory structure under "Raw", comment out the
# lines below and uncommend the commands at the bottom.
arecord -f S16_LE -c${CHANNELS} -r48000 -t wav -d $RECORDING_DURATION \
--use-strftime ${EXTRACTED}/Raw/%B-%Y/%d-%A/%F-birdnet-${STAMP}.wav
# Uncomment the lines below if you'd prefer having no directory scructure
# under the "Raw" directory. Be sure to comment out the command above.
#arecord -f S16_LE -c${CHANNELS} -r48000 -t wav -d $RECORDING_DURATION \
# --use-strftime ${EXTRACTED}/Raw/%F-birdnet-${STAMP}.wav
sleep $PAUSE_DURATION
now=$(date +%H)
done
while [ $now -ge ${CUSTOM_WINDOW_START[1]} ] && [ $now -le ${CUSTOM_WINDOW_END[1]} ];do
echo $now
# If you prefer no directory structure under "Raw", comment out the
# lines below and uncommend the commands at the bottom.
arecord -f S16_LE -c${CHANNELS} -r48000 -t wav -d $RECORDING_DURATION \
--use-strftime ${EXTRACTED}/Raw/%B-%Y/%d-%A/%F-birdnet-${STAMP}.wav
# Uncomment the lines below if you'd prefer having no directory scructure
# under the "Raw" directory. Be sure to comment out the command above.
#arecord -f S16_LE -c${CHANNELS} -r48000 -t wav -d $RECORDING_DURATION \
# --use-strftime ${EXTRACTED}/Raw/%F-birdnet-${STAMP}.wav
sleep $PAUSE_DURATION
now=$(date +%H)
done
while [ $now -ge ${CUSTOM_WINDOW_START[2]} ] && [ $now -le ${CUSTOM_WINDOW_END[2]} ];do
echo $now
# If you prefer no directory structure under "Raw", comment out the
# lines below and uncommend the commands at the bottom.
arecord -f S16_LE -c${CHANNELS} -r48000 -t wav -d $RECORDING_DURATION \
--use-strftime ${EXTRACTED}/Raw/%B-%Y/%d-%A/%F-birdnet-${STAMP}.wav
# Uncomment the lines below if you'd prefer having no directory scructure
# under the "Raw" directory. Be sure to comment out the command above.
#arecord -f S16_LE -c${CHANNELS} -r48000 -t wav -d $RECORDING_DURATION \
# --use-strftime ${EXTRACTED}/Raw/%F-birdnet-${STAMP}.wav
sleep $PAUSE_DURATION
now=$(date +%H)
done
while [ $now -ge ${CUSTOM_WINDOW_START[3]} ] && [ $now -le ${CUSTOM_WINDOW_END[3]} ];do
echo $now
# If you prefer no directory structure under "Raw", comment out the
# lines below and uncommend the commands at the bottom.
arecord -f S16_LE -c${CHANNELS} -r48000 -t wav -d $RECORDING_DURATION \
--use-strftime ${EXTRACTED}/Raw/%B-%Y/%d-%A/%F-birdnet-${STAMP}.wav
# Uncomment the lines below if you'd prefer having no directory scructure
# under the "Raw" directory. Be sure to comment out the command above.
#arecord -f S16_LE -c${CHANNELS} -r48000 -t wav -d $RECORDING_DURATION \
# --use-strftime ${EXTRACTED}/Raw/%F-birdnet-${STAMP}.wav
sleep $PAUSE_DURATION
now=$(date +%H)
done
+9 -21
View File
@@ -1,9 +1,8 @@
#!/home/pi/BirdNET-Pi/birdnet/bin/python3
import mysql.connector as sql
import sqlite3
import os
import configparser
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
@@ -11,25 +10,14 @@ from matplotlib.colors import LogNorm
from datetime import datetime
import textwrap
#Extract DB_PWD from thisrun.txt
with open('/home/pi/BirdNET-Pi/thisrun.txt', 'r') as f:
this_run = f.readlines()
db_pwd = str(str(str([i for i in this_run if i.startswith('DB_PWD')]).split('=')[1]).split('\\')[0])
conn = sqlite3.connect('/home/pi/BirdNET-Pi/scripts/birds.db')
df = pd.read_sql_query("SELECT * from detections", conn)
cursor = conn.cursor()
cursor.execute('SELECT * FROM detections')
table_rows = cursor.fetchall()
db_connection = sql.connect(host='localhost',
database='birds',
user='birder',
password=db_pwd)
db_cursor=db_connection.cursor(dictionary=True)
db_cursor.execute('SELECT * FROM detections')
table_rows = db_cursor.fetchall()
df=pd.DataFrame(table_rows)
#df=pd.DataFrame(table_rows)
#Convert Date and Time Fields to Panda's format
df['Date']=pd.to_datetime(df['Date'])
@@ -97,7 +85,7 @@ heat_frame = pd.DataFrame(data=0, index=heat.index, columns = hours_in_day)
heat=(heat+heat_frame).fillna(0)
#Generatie heatmap plot
plot = sns.heatmap(heat, norm=LogNorm(), annot=True, annot_kws={"fontsize":7}, cmap = pal , square = False, cbar=False, linewidths = 0.5, linecolor = "Grey", ax=axs[1], yticklabels = False)
plot = sns.heatmap(heat, norm=LogNorm(), annot=True, annot_kws={"fontsize":7}, fmt="g", cmap = pal , square = False, cbar=False, linewidths = 0.5, linecolor = "Grey", ax=axs[1], yticklabels = False)
plot.set_xticklabels(plot.get_xticklabels(), rotation = 0, size = 7)
# Set heatmap border
@@ -165,7 +153,7 @@ heat_frame = pd.DataFrame(data=0, index=heat.index, columns = hours_in_day)
heat=(heat+heat_frame).fillna(0)
#Generatie heatmap plot
plot = sns.heatmap(heat, norm=LogNorm(), annot=True, annot_kws={"fontsize":7}, cmap = pal , square = False, cbar=False, linewidths = 0.5, linecolor = "Grey", ax=axs[1], yticklabels = False)
plot = sns.heatmap(heat, norm=LogNorm(), annot=True, fmt="g", annot_kws={"fontsize":7}, cmap = pal , square = False, cbar=False, linewidths = 0.5, linecolor = "Grey", ax=axs[1], yticklabels = False)
plot.set_xticklabels(plot.get_xticklabels(), rotation = 0, size = 7)
# Set heatmap border
-22
View File
@@ -1,22 +0,0 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
$file='/home/pi/BirdNET-Pi/exclude_species_list.txt';
$str=file_get_contents("$file");
$str = preg_replace('/^\h*\v+/m', '', $str);
file_put_contents("$file", "$str");
if (isset($_POST['species']))
foreach($_POST['species'] as $selectedOption) {
$content = file_get_contents("/home/pi/BirdNET-Pi/exclude_species_list.txt");
$newcontent = str_replace($selectedOption, "", "$content");
file_put_contents("/home/pi/BirdNET-Pi/exclude_species_list.txt", "$newcontent");
}
$file='/home/pi/BirdNET-Pi/exclude_species_list.txt';
$str=file_get_contents("$file");
//$str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str);
$str = preg_replace('/^\h*\v+/m', '', $str);
file_put_contents("$file", "$str");
header("Location: {$_SERVER['HTTP_REFERER']}");
exit;
?>
-22
View File
@@ -1,22 +0,0 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
$file='/home/pi/BirdNET-Pi/include_species_list.txt';
$str=file_get_contents("$file");
$str = preg_replace('/^\h*\v+/m', '', $str);
file_put_contents("$file", "$str");
if (isset($_POST['species']))
foreach($_POST['species'] as $selectedOption) {
$content = file_get_contents("/home/pi/BirdNET-Pi/include_species_list.txt");
$newcontent = str_replace($selectedOption, "", "$content");
file_put_contents("/home/pi/BirdNET-Pi/include_species_list.txt", "$newcontent");
}
$file='/home/pi/BirdNET-Pi/include_species_list.txt';
$str=file_get_contents("$file");
//$str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str);
$str = preg_replace('/^\h*\v+/m', '', $str);
file_put_contents("$file", "$str");
header("Location: {$_SERVER['HTTP_REFERER']}");
exit;
?>
+48 -112
View File
@@ -1,121 +1,57 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
font-family: 'Arial', 'Gill Sans', 'Gill Sans MT',
' Calibri', 'Trebuchet MS', 'sans-serif';
box-sizing: border-box;
box-sizing: border-box;
}
/* Create two unequal columns that floats next to each other */
.column {
float: left;
padding: 10px;
}
.first {
width: 45%;
}
.second {
width: 10%;
display: flex;
justify-content: center;
flex-direction: column;
height: 100%;
}
.third {
width: 45%;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
body {
background-color: rgb(119, 196, 135);
}
a {
text-decoration: none;
color: white;
}
.block {
display: block;
font-weight: bold;
width:100%;
border: none;
background-color: #04AA6D;
padding: 20px 20px;
color: white;
font-size: medium;
cursor: pointer;
text-align: center;
}
h2 {
text-align: center;
}
select {
width:100%
}
@media screen and (max-width: 800px) {
.column {
float: none;
width: 100%;
}
}
</style>
<body>
<div class="customlabels column1">
<form action="" method="POST" id="add">
<h3>All Species Labels</h3>
<select name="species[]" id="species" multiple size="auto">
<option>Choose a species below to add to the Excluded Species List</option>
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
$filename = 'scripts/labels.txt';
$eachline = file($filename, FILE_IGNORE_NEW_LINES);
foreach($eachline as $lines){echo
"<option value='".$lines."'>$lines</option>";}
?>
</select>
<input type="hidden" name="add" value="add">
</form>
<div class="customlabels smaller">
<button type="submit" name="view" value="Excluded" form="add">>>ADD>></button>
</div>
</div>
<div class="customlabels column2">
<table><td>
<button type="submit" name="view" value="Excluded" form="add">>>ADD>></button>
<br><br>
<button type="submit" name="view" value="Excluded" form="del">REMOVE</button>
</td></table>
</div>
<div class="customlabels column3">
<form action="" method="POST" id="del">
<h3>Excluded Species List</h3>
<select name="species[]" id="value2" multiple size="auto">
<?php
//remove <script></script> and add php start and close tag
//comment these two lines when code started working fine
error_reporting(E_ALL);
ini_set('display_errors',1);
$filename = 'labels.txt';
$eachlines = file($filename, FILE_IGNORE_NEW_LINES);
?>
<body style="background-color: rgb(119, 196, 135);">
<div class="row">
<div class="column first">
<h2>All Species Labels</h2>
<form action="add_to_exclude.php" method="POST" id="add">
<select name="species[]" id="species" multiple size="30">
<option selected value="base">Please Select</option>
<?php
foreach($eachlines as $lines){
echo "<option value='".$lines."'>$lines</option>";
$filename = '/home/pi/BirdNET-Pi/exclude_species_list.txt';
$eachline = file($filename, FILE_IGNORE_NEW_LINES);
foreach($eachline as $lines){
echo
"<option value='".$lines."'>$lines</option>";
}?>
</select>
</form>
</div>
</select>
<input type="hidden" name="del" value="del">
</form>
<div class="customlabels smaller">
<button type="submit" name="view" value="Excluded" form="del">REMOVE</button>
</div>
</div>
<div class="column second">
<button type="submit" form="add" class="block">Add to list</button><br>
<button type="submit" form="del" class="block">Remove from list</button>
</div>
<div class="column third">
<h2>Excluded Species List</h2>
<form action="del_from_exclude.php" method="POST" id="del">
<select name="species[]" id="value2" multiple size="30">
<option selected value="base">Please Select</option>
<?php
$filename = '/home/pi/BirdNET-Pi/exclude_species_list.txt';
$eachlines = file($filename, FILE_IGNORE_NEW_LINES);
foreach($eachlines as $lines){
echo "<option value='".$lines."'>$lines</option>";
}?>
</form>
</div>
</div>
</body>
+32 -16
View File
@@ -12,29 +12,45 @@ echo "Binary: $binary";
revbinary=$(echo $binary|rev)
if echo $binary | grep 1 ;then
echo "ISSUES DETECTED"
if [ ${revbinary:0:1} -eq 1 ];then
echo "Under-voltage detected"
if [ ${revbinary:0:1} -eq 1 &> /dev/null ];then
message="Under-voltage detected"
echo "$message"
dmesg -H | grep -i voltage
fi
if [ ${revbinary:1:1} -eq 1 ];then
echo "Arm frequency capped"
if [ ${revbinary:1:1} -eq 1 &> /dev/null ];then
message="Arm frequency capped"
echo "$message"
dmesg -H | grep -i frequen
fi
if [ ${revbinary:2:1} -eq 1 ];then
echo "Currently Throttled"
if [ ${revbinary:2:1} -eq 1 &> /dev/null ];then
message="Currently Throttled"
echo "$message"
dmesg -H | grep -i throttl
fi
if [ ${revbinary:3:1} -eq 1 ];then
echo "Soft temperatue limit active"
if [ ${revbinary:3:1} -eq 1 &> /dev/null ];then
message="Soft temperatue limit active"
echo "$message"
dmesg -H | grep -i temperature
fi
if [ ${revbinary:16:1} -eq 1 ];then
echo "Under-voltage has occurred"
if [ ${revbinary:16:1} -eq 1 &> /dev/null ];then
message="Under-voltage has occurred"
echo "$message"
dmesg -H | grep -i voltage
fi
if [ ${revbinary:17:1} -eq 1 ];then
echo "Arm frequency capping has occurred"
if [ ${revbinary:17:1} -eq 1 &> /dev/null ];then
message="Arm frequency capping has occurred"
echo "$message"
dmesg -H | grep -i frequen
fi
if [ ${revbinary:18:1} -eq 1 ];then
echo "Throttling has occurred"
if [ ${revbinary:18:1} -eq 1 &> /dev/null ];then
message="Throttling has occurred"
echo "$message"
dmesg -H | grep -i throttl
fi
if [ ${revbinary:19:1} -eq 1 ];then
echo "Soft temperature limit has occurred"
if [ ${revbinary:19:1} -eq 1 &> /dev/null ];then
message="Soft temperature limit has occurred"
echo "$message"
dmesg -H | grep -i temperature
fi
fi
echo "....................................Clock Speeds................................"
+11 -62
View File
@@ -61,14 +61,18 @@ for h in "${SCAN_DIRS[@]}";do
SCIENTIFIC_NAME=""$(echo ${line} \
| awk -F\; '!/birdnet/{print $3}')""
CONFIDENCE=""$(echo ${line} \
| awk -F\; '{print $5}' \
| cut -d'.' -f2)""
CONFIDENCE_SCORE="${CONFIDENCE:0:2}%"
| awk -F\; '{print $5}')""
#CONFIDENCE_SCORE="${CONFIDENCE:0:2}%"
locale_decimal=$(locale decimal_point)
if [[ $locale_decimal == "." ]]; then
CONFIDENCE_SCORE="$(printf %.0f "$(echo "scale=2; ${CONFIDENCE} * 100" | bc)")"
else
CONFIDENCE_SCORE="$(printf %.0f "$(echo "scale=2; "${CONFIDENCE}" * 100" | bc | tr '.' ',')")"
fi
NEWFILE="${COMMON_NAME// /_}-${CONFIDENCE_SCORE}-${OLDFILE//.wav/.${AUDIOFMT}}"
echo "NEWFILE=$NEWFILE"
NEWSPECIES_BYDATE="${EXTRACTED}/By_Date/${DATE}/${COMMON_NAME// /_}"
NEWSPECIES_BY_COMMON="${EXTRACTED}/By_Common_Name/${COMMON_NAME// /_}"
NEWSPECIES_BY_SCIENCE="${EXTRACTED}/By_Scientific_Name/${SCIENTIFIC_NAME// /_}"
# If the extracted file already exists, move on
if [[ -f "${NEWSPECIES_BYDATE}/${NEWFILE}" ]];then
@@ -85,16 +89,6 @@ for h in "${SCAN_DIRS[@]}";do
# it is created
[[ -d "${NEWSPECIES_BYDATE}" ]] || mkdir -p "${NEWSPECIES_BYDATE}"
# If a directory does not already exist for the species (by-species),
# it is created.
[[ -d "${NEWSPECIES_BY_COMMON}" ]] || mkdir -p "${NEWSPECIES_BY_COMMON}"
# If a directory does not already exist for the species (by-species),
# it is created.
[[ -d "${NEWSPECIES_BY_SCIENCE}" ]] || mkdir -p "${NEWSPECIES_BY_SCIENCE}"
# If there are already 20 extracted entries for a given species
# for today, remove the oldest file and create the new one.
# if [[ "$(find ${NEWSPECIES_BYDATE} | wc -l)" -ge 20 ]];then
@@ -131,9 +125,6 @@ for h in "${SCAN_DIRS[@]}";do
sox "${h}/${OLDFILE}" "${NEWSPECIES_BYDATE}/${NEWFILE}" \
trim "${START}" "${END}"
#ffmpeg -hide_banner -loglevel error -nostdin -i "${h}/${OLDFILE}" \
# -acodec copy -ss "${START}" -to "${END}"\
# "${NEWSPECIES_BYDATE}/${NEWFILE}"
# Create spectrogram for extraction
sox "${NEWSPECIES_BYDATE}/${NEWFILE}" -n remix 1 rate 24k spectrogram \
@@ -141,48 +132,6 @@ for h in "${SCAN_DIRS[@]}";do
-c "${NEWSPECIES_BYDATE}/${NEWFILE}" \
-o "${NEWSPECIES_BYDATE}/${NEWFILE}.png"
# Remove the oldest symbolic links that would make the directory have more
# than 20 entries.
if [[ "$(find ${NEWSPECIES_BY_COMMON} | wc -l)" -ge 40 ]];then
echo "20 ${SPECIES}s, already! Removing the oldest by-species and making a new one"
cd ${NEWSPECIES_BY_COMMON} || exit 1
ls -1t . | tail -n +40 | xargs -r rm -vv
ln -fs "${NEWSPECIES_BYDATE}/${NEWFILE}"\
"${NEWSPECIES_BY_COMMON}/${NEWFILE}"
ln -fs "${NEWSPECIES_BYDATE}/${NEWFILE}.png"\
"${NEWSPECIES_BY_COMMON}/${NEWFILE}.png"
echo "Success! New extraction for ${COMMON_NAME}"
else
# Make symbolic link of the extraction to add to By_Common_Name
ln -fs "${NEWSPECIES_BYDATE}/${NEWFILE}"\
"${NEWSPECIES_BY_COMMON}/${NEWFILE}"
ln -fs "${NEWSPECIES_BYDATE}/${NEWFILE}.png"\
"${NEWSPECIES_BY_COMMON}/${NEWFILE}.png"
fi
# Remove the oldest symbolic links that would made the directory have more
# than 20 entries.
if [[ "$(find ${NEWSPECIES_BY_SCIENCE} | wc -l)" -ge 40 ]];then
echo "20 ${SPECIES}s, already! Removing the oldest by-species and making a new one"
cd ${NEWSPECIES_BY_SCIENCE} || exit 1
ls -1t . | tail -n +40 | xargs -r rm -vv
ln -fs "${NEWSPECIES_BYDATE}/${NEWFILE}"\
"${NEWSPECIES_BY_SCIENCE}/${NEWFILE}"
ln -fs "${NEWSPECIES_BYDATE}/${NEWFILE}.png"\
"${NEWSPECIES_BY_SCIENCE}/${NEWFILE}.png"
echo "Success! New extraction for ${COMMON_NAME}"
else
ln -fs "${NEWSPECIES_BYDATE}/${NEWFILE}"\
"${NEWSPECIES_BY_SCIENCE}/${NEWFILE}"
ln -fs "${NEWSPECIES_BYDATE}/${NEWFILE}.png"\
"${NEWSPECIES_BY_SCIENCE}/${NEWFILE}.png"
fi
# Finally, 'a' is incremented by one to allow multiple extractions per
# species per minute.
#a=$((a + 1))
done < "${TMPFILE}"
# Once each line of the TMPFILE has been processed, the TMPFILE is emptied
@@ -192,11 +141,11 @@ for h in "${SCAN_DIRS[@]}";do
# Rename files that have been processed so that they are not processed on the
# next extraction.
[[ -d "${PROCESSED}" ]] || mkdir "${PROCESSED}"
echo "Moving processed files to ${PROCESSED}"
#echo "Moving processed files to ${PROCESSED}"
mv ${h}/* ${PROCESSED} &> /dev/null || continue
done
echo "Linking Processed files to "${EXTRACTED}/Processed" web directory"
#echo "Linking Processed files to "${EXTRACTED}/Processed" web directory"
# After all audio extractions have taken place, a directory is created to house
# the original WAVE and .txt files used for this extraction processs.
if [[ ! -L ${EXTRACTED}/Processed ]] || [[ ! -e ${EXTRACTED}/Processed ]];then
+6 -6
View File
@@ -1,6 +1,6 @@
<?php
//Default Configuration
$CONFIG = '{"lang":"en","error_reporting":false,"show_hidden":false,"hide_Cols":false,"calc_folder":false,"theme":"light"}';
<?php
//Default Configuration
$CONFIG = '{"lang":"en","error_reporting":false,"show_hidden":true,"hide_Cols":false,"calc_folder":false,"theme":"light"}';
/**
* H3K | Tiny File Manager V2.4.6
@@ -3342,9 +3342,9 @@ function fm_show_nav_path($path)
}
?>
<nav class="navbar navbar-expand-lg <?php echo $getTheme; ?> mb-4 main-nav <?php echo $isStickyNavBar ?>">
<a class="navbar-brand" href="../../">BirdNET-Pi Homepage </a>
<a class="navbar-brand" href="../"><?php echo lng('Tools'); ?></a>
<a class="navbar-brand" href="http://birdnetpi.local:8888" target="_"><?php echo lng('WebTerminal'); ?></a>
<form action="../../views.php" method="POST">
<button style="margin-left:80px;" class="navbar-brand" type="submit" name="view" value="Overview">BirdNET-Pi Homepage</button>
<button class="navbar-brand" type="submit" name="view" value="Tools"><?php echo lng('Tools'); ?></button></form>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
-153
View File
@@ -1,153 +0,0 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
* {
font-family: 'Arial', 'Gill Sans', 'Gill Sans MT',
' Calibri', 'Trebuchet MS', 'sans-serif';
box-sizing: border-box;
}
body {
width: 50%;
margin-left: auto;
margin-right: auto;
background-color: rgb(119, 196, 135);
}
a {
text-decoration: none;
}
.block {
display: block;
width:100%;
border: none;
padding: 10px 10px;
font-size: medium;
cursor: pointer;
text-align: center;
}
select {
font-size:large;
width: 60%;
}
select option {
font-size:large;
}
form {
text-align:left;
padding:10px;
}
h1 {
margin-bottom:0px;
}
h3 {
margin-left: -10px;
}
label {
float:left;
width:40%;
font-weight:bold;
}
input {
width:60%;
text-align:center;
font-size:large;
}
.center {
display: block;
margin-left: auto;
margin-right: auto;
}
@media screen and (max-width: 800px) {
h1, h2 {
text-align:center;
}
form {
text-align:left;
margin-left:0px;
}
select, body, button, input, label {
width:100%;
{
}
</style>
</head>
<?php
if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/thisrun.txt');
} elseif (file_exists('/home/pi/BirdNET-Pi/firstrun.ini')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/firstrun.ini');
} ?>
<h1 style="float:top;text-align:center;">Welcome!</h1>
<body style="background-color: rgb(119, 196, 135);">
<form action="write_config.php" method="POST" name="firstboot">
<p style="font-size:large">Thank you for installing BirdNET-Pi!
to get started, fill out the Required sections below.</p>
<h2>Required</h2>
<label for="latitude">Latitude: </label>
<input name="latitude" type="number" min="-90" max="90" step="0.0001" value="<?php print($config['LATITUDE']);?>" required/><br>
<label for="longitude">Longitude: </label>
<input name="longitude" type="number" min="-180" max="180" step="0.0001" value="<?php print($config['LONGITUDE']);?>" required/><br>
<h2>Optional Services</h2>
<p>The services below are not required, but they are pretty cool.</p>
<label for="birdweather_id">BirdWeather ID: </label>
<input name="birdweather_id" type="text" value="<?php print($config['BIRDWEATHER_ID']);?>" /><br>
<p>app.BirdWeather.com is a weather map for bird sounds. Stations around the world supply audio and video streams to BirdWeather where they are then analyzed by BirdNET and compared to eBird Grid data. BirdWeather catalogues the detections and visualizations so that you can listen to, view, and read about the birds in various regions across the globe! Pretty cool! <a href="mailto:tim@birdweather.com?subject=Request%20BirdWeather%20ID&body=<?php include('birdweather_request.php'); ?>" target="top">Email Tim</a> to request a BirdWeather ID</p>
<label for="pushed_app_key">Pushed App Key: </label>
<input name="pushed_app_key" type="text" value="" /><br>
<label for="pushed_app_secret">Pushed App Secret: </label>
<input name="pushed_app_secret" type="text" value="" /><br>
<p>Pushed.co is used to offer New Species notifications. Sorry Android users, but the Pushed.co Application is only for iOS.</p>
<label for="language">Database Language: </label>
<select name="language">
<option value="none">English</option>
<option value="labels_af.txt">Afrikaans</option>
<option value="labels_ca.txt">Catalan</option>
<option value="labels_cs.txt">Czech</option>
<option value="labels_zh.txt">Chinese</option>
<option value="labels_hr.txt">Croatian</option>
<option value="labels_da.txt">Danish</option>
<option value="labels_nl.txt">Dutch</option>
<option value="labels_et.txt">Estonian</option>
<option value="labels_fi.txt">Finnish</option>
<option value="labels_fr.txt">French</option>
<option value="labels_de.txt">German</option>
<option value="labels_hu.txt">Hungarian</option>
<option value="labels_is.txt">Icelandic</option>
<option value="labels_id.txt">Indonesia</option>
<option value="labels_it.txt">Italian</option>
<option value="labels_ja.txt">Japanese</option>
<option value="labels_lv.txt">Latvian</option>
<option value="labels_lt.txt">Lithuania</option>
<option value="labels_no.txt">Norwegian</option>
<option value="labels_pl.txt">Polish</option>
<option value="labels_pt.txt">Portugues</option>
<option value="labels_ru.txt">Russian</option>
<option value="labels_sk.txt">Slovak</option>
<option value="labels_sl.txt">Slovenian</option>
<option value="labels_es.txt">Spanish</option>
<option value="labels_sv.txt">Swedish</option>
<option value="labels_th.txt">Thai</option>
<option value="labels_uk.txt">Ukrainian</option>
</select>
<br><br>
</body>
<footer>
<button type="submit" name"firstboot" class="block">I Am Finished!</button>
</footer>
</form>
-10
View File
@@ -1,10 +0,0 @@
#!/usr/bin/env bash
# testing getting images dynamically
first_call=$(lynx -nonumbers -dump -listonly "https://commons.wikimedia.org/w/index.php?search=${1//_/+}&title=Special:MediaSearch&go=Go&type=image" | sed '11q;d')
lynx -dump -nonumbers "$(lynx -dump $first_call \
| awk '/CiteThisPage/ {print $2}')" \
| grep -A10 'Chicago style' \
| grep -A10 -e '^$' \
| grep -B10 'CBE' \
| grep -B10 -e '^$'
-10
View File
@@ -1,10 +0,0 @@
#!/usr/bin/env bash
# testing getting images dynamically
first_call=$(lynx -dump -listonly "https://commons.wikimedia.org/w/index.php?search=${1//_/+}&title=Special:MediaSearch&go=Go&type=image")
lynx -dump -listonly \
$(echo "$first_call" \
| sed '11q;d' \
| awk '{print $2}' ) \
| awk '/thumb/ {print $2}' \
| head -1 \
| xargs echo -n
Executable
BIN
View File
Binary file not shown.
+14 -55
View File
@@ -10,85 +10,44 @@ $theDate = date('Y-m-d');
}
$chart = "Combo-$theDate.png";
$chart2 = "Combo2-$theDate.png";
$mysqli = mysqli_connect();
$mysqli->select_db('birds');
if ($mysqli->connect_error) {
die('Connect Error (' .
$mysqli->connect_errno . ') '.
$mysqli->connect_error);
}
$db = new SQLite3('/home/pi/BirdNET-Pi/scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
// SQL query to select data from database
$statement1 = $db->prepare("SELECT COUNT(*) FROM detections
WHERE Date == \"$theDate\"");
$result1 = $statement1->execute();
$totalcount = $result1->fetchArray(SQLITE3_ASSOC);
$sql1 = "SELECT COUNT(*)
FROM detections
WHERE DATE = \"$theDate\"";
$dayscount = $mysqli->query($sql1);
$mysqli->close();
?>
<head>
<link rel="stylesheet" href="style.css">
<style>
input {
width:auto;
}
center {
display: block;
margin-left: auto;
margin-right: auto;
width: 100%;
}
body {
background-color: rgb(119, 196, 135);
}
button,input {
font-size: medium;
}
table,th,td {
background-color: rgb(219, 255, 235);
}
table {
width:30%;
}
hr {
border: 1px solid green;
width:80%;
}
</style>
</head>
<body>
<form style="margin-left: -150px;text-align:center;" action="" name="submit" method="POST">
<div class="history centered">
<form action="" method="POST">
<input type="date" name="date" value="<?php echo $theDate;?>">
<button type="submit" class="block">Submit Date</button>
<button type="submit" name="view" value="Daily Charts">Submit Date</button>
</form>
<div style="margin-left: -150px;">
<table>
<tr>
<th>Total Detections For The Day</th>
<?php
while($row=$dayscount->fetch_assoc()){
?>
<td><?php echo $row['COUNT(*)'];?></td>
<?php
}?>
<td><?php echo $totalcount['COUNT(*)'];?></td>
</tr>
</table>
</div>
<?php
if (file_exists('/home/pi/BirdSongs/Extracted/Charts/'.$chart)) {
echo "<img src=\"/Charts/$chart?nocache=time()\" style=\"height:auto;width: 100%;padding: 5px;margin-left: auto;margin-right: auto;display: block;\">";
echo "<img src=\"/Charts/$chart?nocache=time()\" >";
} else {
echo "<p style=\"text-align:center;margin-left:-150px;\">No Charts for $theDate</p>";
echo "<p>No Charts for $theDate</p>";
}
echo "<hr>";
if (file_exists('/home/pi/BirdSongs/Extracted/Charts/'.$chart2)) {
echo "<img src=\"/Charts/$chart2?nocache=time()\" style=\"height:auto;width: 100%;padding: 5px;margin-left: auto;margin-right: auto;display: block;\">";
echo "<img src=\"/Charts/$chart2?nocache=time()\">";
} else {
echo "<p style=\"text-align:center;margin-left:-150px;\">No Charts For $theDate</p>";
echo "<p>No Charts For $theDate</p>";
}?>
</div>
</html>
+47 -106
View File
@@ -1,121 +1,62 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
font-family: 'Arial', 'Gill Sans', 'Gill Sans MT',
' Calibri', 'Trebuchet MS', 'sans-serif';
box-sizing: border-box;
box-sizing: border-box;
}
/* Create two unequal columns that floats next to each other */
.column {
float: left;
padding: 10px;
}
.first {
width: 45%;
}
.second {
width: 10%;
display: flex;
justify-content: center;
flex-direction: column;
height: 100%;
}
.third {
width: 45%;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
body {
background-color: rgb(119, 196, 135);
}
a {
text-decoration: none;
color: white;
}
.block {
display: block;
font-weight: bold;
width:100%;
border: none;
background-color: #04AA6D;
padding: 20px 20px;
color: white;
font-size: medium;
cursor: pointer;
text-align: center;
}
h2 {
text-align: center;
}
select {
width:100%
}
@media screen and (max-width: 800px) {
.column {
float: none;
width: 100%;
}
}
</style>
<?php
//remove <script></script> and add php start and close tag
//comment these two lines when code started working fine
error_reporting(E_ALL);
ini_set('display_errors',1);
$filename = 'labels.txt';
$filename = 'scripts/labels.txt';
$eachlines = file($filename, FILE_IGNORE_NEW_LINES);
?>
<body style="background-color: rgb(119, 196, 135);">
<div class="row">
<div class="column first">
<h2>All Species Labels</h2>
<form action="add_to_include.php" method="POST" id="add">
<select name="species[]" id="species" multiple size="30">
<option selected value="base">Please Select</option>
<?php
foreach($eachlines as $lines){
echo "<option value='".$lines."'>$lines</option>";
}?>
</select>
</form>
</div>
<div class="customlabels smaller">
<br>
</div>
<body style="height: 90%;">
<p>Warning!<br>If this list contains ANY species, the system will ONLY recognize those species. Keep this list EMPTY unless you are ONLY interested in detecting specific species.</p>
<div class="customlabels2 column1">
<form action="" method="POST" id="add">
<h2>All Species Labels</h2>
<select name="species[]" id="species" multiple size="30">
<option selected value="base">Please Select</option>
<?php
foreach($eachlines as $lines){echo
"<option value='".$lines."'>$lines</option>";}
?>
</select>
<input type="hidden" name="add" value="add">
</form>
<div class="customlabels2 smaller">
<button type="submit" name="view" value="Included" form="add">>>ADD>></button>
</div>
</div>
<div class="column second">
<button type="submit" form="add" class="block">Add to list</button><br>
<button type="submit" form="del" class="block">Remove from list</button>
</div>
<div class="customlabels2 column4">
<table><td>
<button type="submit" name="view" value="Included" form="add">>>ADD>></button>
<br><br>
<button type="submit" name="view" value="Included" form="del">REMOVE</button>
</td></table>
</div>
<div class="column third">
<h2>Included Species List</h2>
<form action="del_from_include.php" method="POST" id="del">
<select name="species[]" id="value2" multiple size="30">
<option selected value="base">Please Select</option>
<?php
$filename = '/home/pi/BirdNET-Pi/include_species_list.txt';
$eachlines = file($filename, FILE_IGNORE_NEW_LINES);
foreach($eachlines as $lines){
echo "<option value='".$lines."'>$lines</option>";
}?>
</form>
</div>
</div>
<div class="customlabels2 column3">
<form action="" method="POST" id="del">
<h2>Custom Species List</h2>
<select name="species[]" id="value2" multiple size="30">
<option selected value="base">Please Select</option>
<?php
$filename = '/home/pi/BirdNET-Pi/include_species_list.txt';
$eachlines = file($filename, FILE_IGNORE_NEW_LINES);
foreach($eachlines as $lines){echo
"<option value='".$lines."'>$lines</option>";}
?>
</select>
<input type="hidden" name="del" value="del">
</form>
<div class="customlabels2 smaller">
<button type="submit" name="view" value="Included" form="del">REMOVE</button>
</div>
</div>
</body>
-90
View File
@@ -1,90 +0,0 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
font-family: 'Arial', 'Gill Sans', 'Gill Sans MT',
' Calibri', 'Trebuchet MS', 'sans-serif';
box-sizing: border-box;
box-sizing: border-box;
}
/* Create two unequal columns that floats next to each other */
.column {
float: left;
padding: 10px;
}
.first {
width: calc(50% - 70px);
}
.second {
width: calc(50% - 30px);
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
body {
background-color: rgb(119, 196, 135);
}
a {
text-decoration: none;
color: white;
}
.block {
display: block;
font-weight: bold;
width:100%;
border: none;
background-color: #04AA6D;
padding: 20px 20px;
color: white;
font-size: medium;
cursor: pointer;
text-align: center;
}
@media screen and (max-width: 1000px) {
.column {
float: none;
width: 100%;
}
}
</style>
<body style="background-color: rgb(119, 196, 135);">
<div class="row">
<div class="column first">
<form action="/scripts/filemanager/filemanager.php" target="_top">
<button type="submit" class="block">File Manager</button>
</form>
<form action="/scripts/adminer.php" target="_top">
<button type="submit" class="block">Database Maintenance</button>
</form>
<form action="/scripts/config.php">
<button type="submit" class="block">Settings</button>
</form>
<form action="/scripts/system_controls.php">
<button type="submit" class="block">System Controls</button>
</form>
</div>
<div class="column second">
<form action="http://birdnetpi.local:8888" target="top">
<button type="submit" class="block">Web Terminal</button>
</form>
<form action="/scripts/service_controls.php">
<button type="submit" class="block">Manage Services</button>
</form>
<form action="/scripts/include_list.php">
<button type="submit" class="block">Included Species List</button>
</form>
<form action="/scripts/exclude_list.php">
<button type="submit" class="block">Excluded Species List</button>
</form>
</div>
</div>
</body>
+4 -68
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
# Install BirdNET script
set -x # debugging
set -x
exec > >(tee -i installation-$(date +%F).txt) 2>&1
set -e # exit installation if anything fails
my_dir=$(realpath $(dirname $0))
trap '${my_dir}/dump_logs.sh && echo -e "\n\nExiting the installation. Goodbye!" && exit 1' SIGINT
@@ -20,80 +21,15 @@ fi
sudo ./install_services.sh || exit 1
source /etc/birdnet/birdnet.conf
APT_DEPS=(swig ffmpeg wget unzip curl cmake make bc)
LIBS_MODULES=(libjpeg-dev zlib1g-dev python3-dev python3-pip python3-venv)
install_deps() {
echo " Checking dependencies"
sudo apt update &> /dev/null
for i in "${LIBS_MODULES[@]}";do
if [ $(apt list --installed 2>/dev/null | grep "$i" | wc -l) -le 0 ];then
echo " Installing $i"
sudo apt -y install ${i} &> /dev/null
else
echo " $i is installed!"
fi
done
for i in "${APT_DEPS[@]}";do
if ! which $i &>/dev/null ;then
echo " Installing $i"
sudo apt -y install ${i} &> /dev/null
else
echo " $i is installed!"
fi
done
}
install_birdnet() {
cd ~/BirdNET-Pi || exit 1
echo "Establishing a python virtual environment"
python3 -m venv birdnet
source ./birdnet/bin/activate
echo "Upgrading pip, wheel, and setuptools"
pip3 install --upgrade pip~=21.0.0 wheel setuptools
python_version="$(awk -F. '{print $2}' <(ls -l $(which /usr/bin/python3)))"
echo "python_version=${python_version}"
# TFLite Pre-built binaires from https://github.com/PINTO0309/TensorflowLite-bin
# Python 3.7
if [[ "$python_version" == 7 ]];then
echo "Installing the TFLite bin wheel"
pip3 install --upgrade tflite_runtime-2.6.0-cp37-none-linux_aarch64.whl
fi
# Python 3.9
if [[ "$python_version" == 9 ]];then
echo "Installing the TFLite bin wheel"
pip3 install --upgrade tflite_runtime-2.6.0-cp39-none-linux_aarch64.whl
fi
echo "Making sure everything else is installed"
pip3 install -U -r /home/pi/BirdNET-Pi/requirements.txt
}
[ -d ${RECS_DIR} ] || mkdir -p ${RECS_DIR} &> /dev/null
install_deps
if [ ! -d ${VENV} ];then
install_birdnet
fi
echo " BirdNet is installed!!
To start the service manually, issue:
'sudo systemctl start birdnet_analysis'
To monitor the service logs, issue:
'journalctl -fu birdnet_analysis'
To stop the service manually, issue:
'sudo systemctl stop birdnet_analysis'
To stop and disable the service, issue:
'sudo systemctl disable --now birdnet_analysis.service'
Visit
the BirdNET-Pi homepage at http://birdnetpi.local"
echo
case $YN in
[Yy] ) sudo systemctl start birdnet_analysis.service \
&& journalctl -fu birdnet_analysis;;
* ) echo " Thanks for installing BirdNET-Pi!!
I hope it was helpful!";;
esac
install_birdnet
exit 0
+73 -294
View File
@@ -6,240 +6,63 @@ trap 'exit 1' SIGINT SIGHUP
my_dir=$(realpath $(dirname $0))
birdnetpi_dir=$(realpath $(dirname $my_dir))
BIRDNET_CONF="$(dirname ${my_dir})/birdnet.conf"
birdnet_conf="$(dirname ${my_dir})/birdnet.conf"
get_RECS_DIR() {
read -p "What is the full path to your recordings directory (locally)? " RECS_DIR
}
get_LATITUDE() {
read -p "What is the latitude where the recordings were made? " LATITUDE
}
get_LONGITUDE() {
read -p "What is the longitude where the recordings were made? " LONGITUDE
}
get_DO_EXTRACTIONS() {
while true; do
read -n1 -p "Do you want this device to perform the extractions? " DO_EXTRACTIONS
echo
case $DO_EXTRACTIONS in
[Yy] ) break;;
[Nn] ) break;;
* ) echo "You must answer with Yes or No (y or n)";;
esac
done
}
get_DO_RECORDING() {
while true; do
read -n1 -p "Is this device also doing the recording? " DO_RECORDING
echo
case $DO_RECORDING in
[Yy] ) break;;
[Nn] ) break;;
* ) echo "You must answer with Yes or No (y or n)";;
esac
done
}
get_BIRDNETPI_URL() {
while true;do
read -n1 -p "Would you like to access the extractions via a web browser?
*Note: It is recommended, (but not required), that you run the web
server on the same host that does the extractions. If the extraction
service and web server are on different hosts, the \"By_Species\" and
\"Processed\" symbolic links won't work. The \"By-Date\" extractions,
however, will work as expected." CADDY_SERVICE
echo
case $CADDY_SERVICE in
[Yy] ) read -p "What URL would you like to publish the extractions to?
*Note: Set this to http://localhost if you do not want to make the
extractions publically available: " BIRDNETPI_URL
get_CADDY_PWD
get_ICE_PWD
break;;
[Nn] ) BIRDNETPI_URL= CADDY_PWD= ICE_PWD=;break;;
* ) echo "Please answer Yes or No";;
esac
done
}
get_CADDY_PWD() {
if [ -z ${CADDY_PWD} ]; then
while true; do
read -p "Please set a password to protect your data: " CADDY_PWD
case $CADDY_PWD in
"" ) echo "The password cannot be empty. Please try again.";;
* ) break;;
esac
done
fi
}
get_ICE_PWD() {
if [ ! -z ${CADDY_PWD} ] && [[ ${DO_RECORDING} =~ [Yy] ]];then
while true; do
read -n1 -p "Would you like to enable the live audio streaming service?" LIVE_STREAM
echo
case $LIVE_STREAM in
[Yy] )
read -p "Please set the icecast password. Use only alphanumeric characters." ICE_PWD
echo
case ${ICE_PWD} in
"" ) echo "The password cannot be empty. Please try again.";;
*) break;;
esac
break;;
[Nn] ) break;;
* ) echo "You must answer Yes or No (y or n).";;
esac
done
fi
}
get_DB_PWDS() {
if [ ! -z ${DB_PWD} ];then
read -p "Please set a password for your database: " DB_PWD
echo
fi
}
get_PUSHED() {
while true; do
read -n1 -p "Do you have a free App key to receive mobile notifications via Pushed.co?" YN
echo
case $YN in
[Yy] ) read -p "Enter your Pushed.co App Key: " PUSHED_APP_KEY
read -p "Enter your Pushed.co App Key Secret: " PUSHED_APP_SECRET
break;;
[Nn] ) PUSHED_APP_KEY=
PUSHED_APP_SECRET=
break;;
* ) echo "A simple Yea or Nay will do";;
esac
done
}
get_INSTALL_NOMACHINE() {
while true; do
read -n1 -p "Would you like to also install NoMachine for remote desktop access?" INSTALL_NOMACHINE
echo
case $INSTALL_NOMACHINE in
[Yy] ) break;;
[Nn] ) break;;
* ) echo "You must answer with Yes or No (y or n)";;
esac
done
}
get_CHANNELS() {
REC_CARD="$(sudo -u pi aplay -L \
| grep dsnoop \
| cut -d, -f1 \
| grep -ve 'vc4' -e 'Head' -e 'PCH' \
| uniq)"
[ -f $(dirname ${my_dir})/soundcard_params.txt ] || touch $(dirname ${my_dir})/soundcard_params.txt
SOUND_PARAMS=$(dirname ${my_dir})/soundcard_params.txt
SOUND_CARD="$(sudo -u ${USER} aplay -L \
| awk -F, '/^hw:/ {print $1}' \
| grep -ve 'vc4' -e 'Head' -e 'PCH' \
| uniq)"
script -c "arecord -D ${SOUND_CARD} --dump-hw-params" -a "${SOUND_PARAMS}" &> /dev/null
CHANNELS=$(awk '/CHANN/ { print $2 }' "${SOUND_PARAMS}" | sed 's/\r$//')
[ -z REC_CARD ] && REC_CARD=default
[ -z CHANNELS ] && CHANNELS=2
echo "REC_CARD variable set to ${REC_CARD}"
echo "Number of channels available: ${CHANNELS}"
}
configure() {
get_RECS_DIR
get_LATITUDE
get_LONGITUDE
get_DB_PWDS
get_DO_EXTRACTIONS
get_DO_RECORDING
get_BIRDNETPI_URL
get_PUSHED
get_INSTALL_NOMACHINE
get_CHANNELS
}
install_birdnet_conf() {
install_config() {
cat << EOF > $(dirname ${my_dir})/birdnet.conf
################################################################################
# Configuration settings for BirdNET-Pi #
################################################################################
############ CHANGE THE LATITUDE AND LONGITUDE TO STATIC VALUES ################
#--------------------- Required: Latitude, and Longitude ----------------------#
## The shell substitution below guesses these based on your network. THESE NEED
## TO BE CHANGED TO STATIC VALUES
## Please only go to 4 decimal places. Example:43.3984
## LATITUDE and LONGITUDE are self-explanatroy. Find them easily at
## maps.google.com.
## Example: these coordinates would indicate the Eiffel Tower in Paris, France.
## LATITUDE=48.858
## LONGITUDE=2.294
LATITUDE=$(curl -s4 ifconfig.co/json | awk '/lat/ {print $2}' | tr -d ',')
LONGITUDE=$(curl -s4 ifconfig.co/json | awk '/lon/ {print $2}' | tr -d ',')
## These are input as shell substitutions so that this will work out of the box.
## It guesses your latitude and longitude based off of your network information.
## THESE SHOULD BE CHANGED TO STATIC NUMBERS!!!!
LATITUDE=${LATITUDE}
LONGITUDE=${LONGITUDE}
#--------------------- BirdWeather Station Information -----------------------#
#_____________The variable below can be set to have your BirdNET-Pi____________#
#__________________also act as a BirdWeather listening station_________________#
## RECS_DIR is the location birdnet_analysis.service will look for the data-set
## it needs to analyze. Be sure this directory is readable and writable for
## the BIRDNET_USER. If you are going to be accessing a remote data-set, you
## still need to set this, as this will be where the remote directory gets
## mounted locally.
RECS_DIR=${RECS_DIR}
BIRDWEATHER_ID=
#----------------------- Web Interface User Password ------------------------#
#____________________The variable below sets the 'birdnet'_____________________#
#___________________user password for the live audio stream,___________________#
#_________________web tools, system info, and processed files__________________#
#____________________The variable below sets the 'birdnet'_____________________#
#___________________user password for the Live Audio Stream,___________________#
#_________________Tools, System Links, and the Processed files ________________#
## CADDY_PWD is the plaintext password (that will be hashed) and used to access
## certain parts of the web interface
CADDY_PWD=${CADDY_PWD}
#------------------------- MariaDB User Passwords ---------------------------#
#_____________The variable below sets the password for the_____________________#
#_______________________'birder' user on the MariaDB___________________________#
## DB_PWD is for the 'birder' user
DB_PWD=${DB_PWD}
CADDY_PWD=
#------------------------- Live Audio Stream --------------------------------#
#_____________The variable below configures/enables the live___________________#
#_____________The variable below configures/enables the live___________________#
#_____________________________audio stream.____________________________________#
## ICE_PWD is the password that icecast2 will use to authenticate ffmpeg as a
## trusted source for the stream. You will never need to enter this manually
## anywhere other than here.
## anywhere other than here and it stays on 'localhost.'
ICE_PWD=${ICE_PWD}
ICE_PWD=birdnetpi
#----------------------- Web-hosting/Caddy File-server -----------------------#
#________The four variables below can be set to enable internet access_________#
#_______The three variables below can be set to enable internet access_________#
#____________to your data,(e.g., extractions, raw data, live___________________#
#______________audio stream, BirdNET.selection.txt files)______________________#
## BIRDNETPI_URL is the URL where the extractions, data-set, and live-stream
## will be web-hosted. If you do not own a domain, or would just prefer to keep
## will be web-hosted. If you do not own a domain, or would just prefer to keep
## the BirdNET-Pi on your local network, keep this EMPTY.
BIRDNETPI_URL=${BIRDNETPI_URL}
WEBTERMINAL_URL${WEBTERMINAL_URL}
BIRDNETLOG_URL=${BIRDNETLOG_URL}
BIRDNETPI_URL=
BIRDNETLOG_URL=
WEBTERMINAL_URL=
#------------------- Mobile Notifications via Pushed.co ---------------------#
@@ -251,97 +74,54 @@ BIRDNETLOG_URL=${BIRDNETLOG_URL}
## Pushed.co App Key and App Secret
PUSHED_APP_KEY=${PUSHED_APP_KEY}
PUSHED_APP_SECRET=${PUSHED_APP_SECRET}
PUSHED_APP_KEY=
PUSHED_APP_SECRET=
################################################################################
#-------------------------------- Defaults ----------------------------------#
################################################################################
#------------------------------- NoMachine ----------------------------------#
#_____________The variable below can be set include NoMachine__________________#
#_________________remote desktop software to be installed._____________________#
## RECS_DIR is the location birdnet_analysis.service will look for the data-set
## it needs to analyze. Be sure this directory is readable and writable for
## the BIRDNET_USER.
# Keep this EMPTY if you do not want to install NoMachine. #
RECS_DIR=/home/pi/BirdSongs
## INSTALL_NOMACHINE is simply a setting that can be enabled to install
## NoMachine alongside the BirdNET-Pi for remote desktop access. This in-
## staller assumes personal use. Please reference the LICENSE file included
## in this repository for more information.
## Set this to Y or y to install NoMachine alongside the BirdNET-Lite
## REC_CARD is the sound card you would want the birdnet_recording.service to
## use. Leave this as "default" to use PulseAudio (recommended), or use
## the output from "aplay -L" to specify an ALSA device.
INSTALL_NOMACHINE=${INSTALL_NOMACHINE}
REC_CARD=default
#
#------------------------------ Extraction Service ---------------------------#
## DO_EXTRACTIONS is simply a setting for enabling the extraction.service.
## Set this to Y or y to enable extractions.
DO_EXTRACTIONS=${DO_EXTRACTIONS}
#----------------------------- Recording Service ----------------------------#
#____________________The variable below can be set to enable __________________#
#________________________the birdnet_recording.service ________________________#
## DO_RECORDING is simply a setting for enabling the 24/7
## birdnet_recording.service.
## Set this to Y or y to enable recording.
DO_RECORDING=${DO_RECORDING}
## REC_CARD is the sound card you would want the birdnet_recording.service to
## use. This setting is irrelevant if you are not planning on doing data
## collection via recording on this machine. The command substitution below
## looks for a USB microphone's dsnoop alsa device. The dsnoop device lets
## birdnet_recording.service and livestream.service share the raw audio stream
## from the microphone. If you would like to use a different microphone than
## what this produces, or if your microphone does not support creating a
## dsnoop device, you can set this explicitly from a list of the available
## devices from the output of running 'aplay -L'
REC_CARD=${REC_CARD}
## PROCESSED is the directory where the formerly 'Analyzed' files are moved
## after extractions have been made from them. This includes both WAVE and
## PROCESSED is the directory where the formerly 'Analyzed' files are moved
## after extractions have been made from them. This includes both WAVE and
## BirdNET.selection.txt files.
PROCESSED=${PROCESSED}
PROCESSED=/home/pi/BirdSongs/Processed
## EXTRACTED is the directory where the extracted audio selections are moved.
EXTRACTED=${EXTRACTED}
## IDFILE is the file that keeps a complete list of every spececies that
## BirdNET has identified from your data-set. It is persistent across
## data-sets, so would need to be whiped clean through deleting or renaming
## it. A backup is automatically made from this variable each time it is
## updated (structure: ${IDFILE}.bak), and would also need to be removed
## or renamed to start a new file between data-sets. Alternately, you can
## change this variable between data-sets to preserve records of disparate
## data-sets according to name.
IDFILE=${IDFILE}
EXTRACTED=/home/pi/BirdSongs/Extracted
## OVERLAP is the value in seconds which BirdNET should use when analyzing
## the data. The values must be between 0.0-2.9.
OVERLAP=${OVERLAP}
OVERLAP=0.0
## CONFIDENCE is the minimum confidence level from 0.0-1.0 BirdNET's analysis
## CONFIDENCE is the minimum confidence level from 0.0-1.0 BirdNET's analysis
## should reach before creating an entry in the BirdNET.selection.txt file.
## Don't set this to 1.0 or you won't have any results.
CONFIDENCE=${CONFIDENCE}
CONFIDENCE=0.7
## SENSITIVITY is the detection sensitivity from 0.5-1.5.
SENSITIVITY=${SENSITIVITY}
SENSITIVITY=1.25
## CHANNELS holds the variabel that corresponds to the number of channels the
## CHANNELS holds the variable that corresponds to the number of channels the
## sound card supports.
CHANNELS=${CHANNELS}
CHANNELS=2
## FULL_DISK can be set to configure how the system reacts to a full disk
## purge = Remove the oldest day's worth of recordings
@@ -349,46 +129,45 @@ CHANNELS=${CHANNELS}
FULL_DISK=purge
## VENV is the virtual environment where the the BirdNET python build is found,
## i.e, VENV is the virtual environment miniforge built for BirdNET.
## RECORDING_LENGTH sets the length of the recording that BirdNET-Lite will
## analyze.
VENV=/home/pi/BirdNET-Pi/birdnet
## RECORDING_LENGTH sets the length of the recording that BirdNET-Lite will analyze.
RECORDING_LENGTH=${RECORDING_LENGTH}
RECORDING_LENGTH=15
## EXTRACTION_LENGTH sets the length of the audio extractions that will be made
## from each BirdNET-Lite detection.
EXTRACTION_LENGTH=${EXTRACTION_LENGTH}
## from each BirdNET-Lite detection. An empty value will use the default of 6
## seconds.
## BIRDNET_USER should be the non-root user systemd should use to execute each
EXTRACTION_LENGTH=
## AUDIOFMT set the audio format that sox should use for the extractions.
## The default is mp3. Available formats are: 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
## Note: Most have not been tested.
AUDIOFMT=mp3
## BIRDNET_USER should be the non-root user systemd should use to execute each
## service.
BIRDNET_USER=pi
## These are just for debugging
LAST_RUN=${LAST_RUN}
THIS_RUN=${THIS_RUN}
LAST_RUN=
THIS_RUN=
IDFILE=/home/pi/BirdNET-Pi/IdentifiedSoFar.txt
EOF
}
# Checks for a birdnet.conf file in the BirdNET-Pi directory for a
# non-interactive installation. Otherwise,the installation is interactive.
if [ -f ${BIRDNET_CONF} ];then
source ${BIRDNET_CONF}
chmod g+w ${BIRDNET_CONF}
sed -i "s/LATITUDE=.*/LATITUDE=${LATITUDE}/g" ${BIRDNET_CONF}
sed -i "s/LONGITUDE=.*/LONGITUDE=${LONGITUDE}/g" ${BIRDNET_CONF}
sed -i "s/DB_PWD=.*/DB_PWD=${DB_PWD}/g" ${BIRDNET_CONF}
#install_birdnet_conf
[ -d /etc/birdnet ] || sudo mkdir /etc/birdnet
sudo ln -sf $(dirname ${my_dir})/birdnet.conf /etc/birdnet/birdnet.conf
grep -ve '^#' -e '^$' /etc/birdnet/birdnet.conf > ${birdnetpi_dir}/firstrun.ini
else
configure
install_birdnet_conf
chmod g+w ${BIRDNET_CONF}
[ -d /etc/birdnet ] || sudo mkdir /etc/birdnet
sudo ln -sf $(dirname ${my_dir})/birdnet.conf /etc/birdnet/birdnet.conf
grep -ve '^#' -e '^$' /etc/birdnet/birdnet.conf > ${birdnetpi_dir}/firstrun.ini
# Checks for a birdnet.conf file
if ! [ -f ${birdnet_conf} ];then
install_config
fi
chmod g+w ${birdnet_conf}
[ -d /etc/birdnet ] || sudo mkdir /etc/birdnet
sudo ln -sf $(dirname ${my_dir})/birdnet.conf /etc/birdnet/birdnet.conf
grep -ve '^#' -e '^$' /etc/birdnet/birdnet.conf > ${birdnetpi_dir}/firstrun.ini
+141 -284
View File
@@ -7,60 +7,38 @@ USER=pi
HOME=/home/pi
my_dir=${HOME}/BirdNET-Pi/scripts
tmpfile=$(mktemp)
nomachine_url="https://download.nomachine.com/download/7.7/Arm/nomachine_7.7.4_1_arm64.deb"
gotty_url="https://github.com/yudai/gotty/releases/download/v1.0.1/gotty_linux_arm.tar.gz"
config_file="$(dirname ${my_dir})/birdnet.conf"
install_depends() {
curl -1sLf \
'https://dl.cloudsmith.io/public/caddy/stable/setup.deb.sh' \
| sudo -E bash
apt -qqq update && apt -qqy upgrade
echo "icecast2 icecast2/icecast-setup boolean false" | debconf-set-selections
apt install -qqy caddy ftpd sqlite3 php-sqlite3 alsa-utils \
pulseaudio avahi-utils sox libsox-fmt-mp3 php php-fpm php-curl php-xml \
php-zip icecast2 swig ffmpeg wget unzip curl cmake make bc libjpeg-dev \
zlib1g-dev python3-dev python3-pip python3-venv
}
set_hostname() {
if [ "$(hostname)" == "raspberrypi" ];then
echo "Setting hostname to 'birdnetpi'"
hostnamectl set-hostname birdnetpi
sed -i 's/raspberrypi/birdnetpi/g' /etc/hosts
fi
}
install_lynx() {
if ! which lynx &> /dev/null;then
apt -y install lynx
fi
}
install_ftpd() {
if ! [ -f /etc/ftpuseres ];then
apt -y install ftpd
fi
}
update_system() {
apt update && apt -y upgrade
update_etc_hosts() {
sed -ie s/'$(hostname).local'/"$(hostname).local ${BIRDNETPI_URL//https:\/\/} ${WEBTERMINAL_URL//https:\/\/} ${BIRDNETLOG_URL//https:\/\/}"/g /etc/hosts
}
install_scripts() {
echo "Installing BirdNET-Pi scripts to /usr/local/bin"
ln -sf ${my_dir}/* /usr/local/bin/
rm /usr/local/bin/index.html
}
install_mariadb() {
if ! which mysql &> /dev/null;then
echo "Installing MariaDB Server"
apt -qqy update
apt -qqy install mariadb-server
echo "MariaDB Installed"
fi
echo "Initializing the database"
source /etc/os-release
if [[ "${VERSION_CODENAME}" == "buster" ]];then
USER=${USER} ${my_dir}/createdb_buster.sh
elif [[ "${VERSION_CODENAME}" == "bullseye" ]];then
USER=${USER} ${my_dir}/createdb_bullseye.sh
fi
systemctl restart php${php_version}-fpm
}
install_birdnet_analysis() {
echo "Installing the birdnet_analysis.service"
cat << EOF > /etc/systemd/system/birdnet_analysis.service
cat << EOF > /home/pi/BirdNET-Pi/templates/birdnet_analysis.service
[Unit]
Description=BirdNET Analysis
After=birdnet_server.service
@@ -74,12 +52,12 @@ ExecStart=/usr/local/bin/birdnet_analysis.sh
[Install]
WantedBy=multi-user.target
EOF
ln -sf /home/pi/BirdNET-Pi/templates/birdnet_analysis.service /usr/lib/systemd/system
systemctl enable birdnet_analysis.service
}
install_birdnet_server() {
echo "Installing the birdnet_server.service"
cat << EOF > /etc/systemd/system/birdnet_server.service
cat << EOF > /home/pi/BirdNET-Pi/templates/birdnet_server.service
[Unit]
Description=BirdNET Analysis Server
Before=birdnet_analysis.service
@@ -92,12 +70,12 @@ ExecStart=/usr/local/bin/server.py
[Install]
WantedBy=multi-user.target
EOF
ln -sf /home/pi/BirdNET-Pi/templates/birdnet_server.service /usr/lib/systemd/system
systemctl enable birdnet_server.service
}
install_extraction_service() {
echo "Installing the extraction.service and extraction.timer"
cat << EOF > /etc/systemd/system/extraction.service
cat << EOF > /home/pi/BirdNET-Pi/templates/extraction.service
[Unit]
Description=BirdNET BirdSound Extraction
[Service]
@@ -105,27 +83,16 @@ Restart=on-failure
RestartSec=3
Type=simple
User=${USER}
ExecStart=/usr/local/bin/extract_new_birdsounds.sh
ExecStart=/usr/bin/env bash -c 'while true;do extract_new_birdsounds.sh;sleep 3;done'
[Install]
WantedBy=multi-user.target
EOF
cat << EOF > /etc/systemd/system/extraction.timer
[Unit]
Description=BirdNET BirdSound Extraction Timer
Requires=extraction.service
[Timer]
Unit=extraction.service
OnCalendar=*:*:0/10
[Install]
WantedBy=multi-user.target
EOF
systemctl enable extraction.timer
ln -sf /home/pi/BirdNET-Pi/templates/extraction.service /usr/lib/systemd/system
systemctl enable extraction.service
}
install_pushed_notifications() {
echo "Installing Pushed.co mobile notifications"
cat << EOF > /etc/systemd/system/pushed_notifications.service
cat << EOF > /home/pi/BirdNET-Pi/templates/pushed_notifications.service
[Unit]
Description=BirdNET-Pi Pushed.co Notifications
[Service]
@@ -137,6 +104,7 @@ ExecStart=/usr/local/bin/species_notifier.sh
[Install]
WantedBy=multi-user.target
EOF
ln -sf /home/pi/BirdNET-Pi/templates/pushed_notifications.service /usr/lib/systemd/system
systemctl enable pushed_notifications.service
}
@@ -144,61 +112,23 @@ create_necessary_dirs() {
echo "Creating necessary directories"
[ -d ${EXTRACTED} ] || sudo -u ${USER} mkdir -p ${EXTRACTED}
[ -d ${EXTRACTED}/By_Date ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/By_Date
[ -d ${EXTRACTED}/By_Common_Name ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/By_Common_Name
[ -d ${EXTRACTED}/By_Scientific_Name ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/By_Scientific_Name
[ -d ${EXTRACTED}/Charts ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/Charts
[ -d ${PROCESSED} ] || sudo -u ${USER} mkdir -p ${PROCESSED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/homepage/* ${EXTRACTED}
if [ ! -z ${BIRDNETLOG_URL} ];then
BIRDNETLOG_URL="$(echo ${BIRDNETLOG_URL} | sed 's/\/\//\\\/\\\//g')"
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*/*.php
fi
if [ ! -z ${WEBTERMINAL_URL} ];then
WEBTERMINAL_URL="$(echo ${WEBTERMINAL_URL} | sed 's/\/\//\\\/\\\//g')"
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*/*.php
fi
sudo -u ${USER} ln -fs $(dirname ${my_dir})/model/labels.txt ${my_dir}/
sudo -u ${USER} ln -fs $(dirname ${my_dir})/model/labels.txt ${my_dir}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts ${EXTRACTED}
if [ -z ${BIRDNETPI_URL} ];then
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*/*.php
else
BIRDNETPI_URL="$(echo ${BIRDNETPI_URL} | sed 's/\/\//\\\/\\\//g')"
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*/*.php
fi
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/play.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/spectrogram.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/viewday.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/overview.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/stats.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/viewdb.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/history.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/homepage/images/favicon.ico ${EXTRACTED}
sudo -u ${USER} ln -fs ${HOME}/phpsysinfo ${EXTRACTED}
sudo -u ${USER} cp -f $(dirname ${my_dir})/templates/phpsysinfo.ini ${HOME}/phpsysinfo/
sudo -u ${USER} cp -f $(dirname ${my_dir})/templates/green_bootstrap.css ${HOME}/phpsysinfo/templates/
sudo -u ${USER} cp -f $(dirname ${my_dir})/templates/index_bootstrap.html ${HOME}/phpsysinfo/templates/html
echo "Setting Wttr.in URL to "${LATITUDE}", "${LONGITUDE}""
sudo -u${USER} sed -i "s/https:\/\/v2.wttr.in\//https:\/\/v2.wttr.in\/"${LATITUDE},${LONGITUDE}"/g" $(dirname ${my_dir})/homepage/menu.html
sudo -u ${USER} ln -fs $(dirname ${my_dir})/templates/phpsysinfo.ini ${HOME}/phpsysinfo/
sudo -u ${USER} ln -fs $(dirname ${my_dir})/templates/green_bootstrap.css ${HOME}/phpsysinfo/templates/
sudo -u ${USER} ln -fs $(dirname ${my_dir})/templates/index_bootstrap.html ${HOME}/phpsysinfo/templates/html
chmod -R g+rw $(dirname ${my_dir})
chmod -R g+rw ${RECS_DIR}
}
@@ -212,27 +142,10 @@ generate_BirdDB() {
sudo -u ${USER} sed -i '1 i\Date;Time;Sci_Name;Com_Name;Confidence;Lat;Lon;Cutoff;Week;Sens;Overlap' $(dirname ${my_dir})/BirdDB.txt
fi
ln -sf $(dirname ${my_dir})/BirdDB.txt ${my_dir}/BirdDB.txt &&
chown pi:pi ${my_dir}/BirdDB.txt && chmod g+rw ${my_dir}/BirdDB.txt
chown pi:pi ${my_dir}/BirdDB.txt && chmod g+rw ${my_dir}/BirdDB.txt
}
install_alsa() {
echo "Checking for alsa-utils and pulseaudio"
if which arecord &> /dev/null ;then
echo "alsa-utils installed"
else
echo "Installing alsa-utils"
apt -qqq update
apt install -qqy alsa-utils
echo "alsa-utils installed"
fi
if which pulseaudio &> /dev/null;then
echo "PulseAudio installed"
else
echo "Installing pulseaudio"
apt -qqq update
apt install -qqy pulseaudio
echo "PulseAudio installed"
fi
set_login() {
if ! [ -d /etc/lightdm ];then
systemctl set-default multi-user.target
ln -fs /lib/systemd/system/getty@.service /etc/systemd/system/getty.target.wants/getty@tty1.service
@@ -246,7 +159,7 @@ EOF
install_recording_service() {
echo "Installing birdnet_recording.service"
cat << EOF > /etc/systemd/system/birdnet_recording.service
cat << EOF > /home/pi/BirdNET-Pi/templates/birdnet_recording.service
[Unit]
Description=BirdNET Recording
[Service]
@@ -259,39 +172,45 @@ ExecStart=/usr/local/bin/birdnet_recording.sh
[Install]
WantedBy=multi-user.target
EOF
ln -sf /home/pi/BirdNET-Pi/templates/birdnet_recording.service /usr/lib/systemd/system
systemctl enable birdnet_recording.service
}
install_caddy() {
if ! which caddy &> /dev/null ;then
echo "Installing Caddy"
curl -1sLf \
'https://dl.cloudsmith.io/public/caddy/stable/setup.deb.sh' \
| sudo -E bash
apt -qq update
apt install -qqy caddy=2.4.5 && apt-mark hold caddy
systemctl enable --now caddy
usermod -aG pi caddy
else
echo "Caddy is installed"
systemctl enable --now caddy
usermod -aG pi caddy
fi
install_custom_recording_service() {
echo "Installing custom_recording.service"
cat << EOF > /home/pi/BirdNET-Pi/templates/custom_recording.service
[Unit]
Description=BirdNET Custom Recording
[Service]
Environment=XDG_RUNTIME_DIR=/run/user/1000
Restart=always
Type=simple
RestartSec=3
User=${USER}
ExecStart=/usr/local/bin/custom_recording.sh
[Install]
WantedBy=multi-user.target
EOF
ln -sf /home/pi/BirdNET-Pi/templates/birdnet_recording.service /usr/lib/systemd/system
}
install_Caddyfile() {
echo "Installing the Caddyfile"
[ -d /etc/caddy ] || mkdir /etc/caddy
if [ -f /etc/caddy/Caddyfile ];then
cp /etc/caddy/Caddyfile{,.original}
fi
php_version="$(awk -F'php' '{print $3}' <(ls -l $(which /etc/alternatives/php)))"
if ! [ -z ${CADDY_PWD} ];then
HASHWORD=$(caddy hash-password -plaintext ${CADDY_PWD})
cat << EOF > /etc/caddy/Caddyfile
http://localhost http://$(hostname).local ${BIRDNETPI_URL} {
root * ${EXTRACTED}
file_server browse
handle /By_Date/* {
file_server browse
}
handle /Charts/* {
file_server browse
}
basicauth /Processed* {
birdnet ${HASHWORD}
}
@@ -304,8 +223,14 @@ http://localhost http://$(hostname).local ${BIRDNETPI_URL} {
basicauth /phpsysinfo* {
birdnet ${HASHWORD}
}
basicauth /terminal* {
birdnet ${HASHWORD}
}
reverse_proxy /stream localhost:8000
php_fastcgi unix//run/php/php${php_version}-fpm.sock
php_fastcgi unix//run/php/php7.4-fpm.sock
reverse_proxy /log* localhost:8080
reverse_proxy /stats* localhost:8501
reverse_proxy /terminal* localhost:8888
}
EOF
else
@@ -313,51 +238,27 @@ EOF
http://localhost http://$(hostname).local ${BIRDNETPI_URL} {
root * ${EXTRACTED}
file_server browse
reverse_proxy /stream localhost:8000
php_fastcgi unix//run/php/php${php_version}-fpm.sock
}
EOF
fi
if [ ! -z ${WEBTERMINAL_URL} ] && [ ! -z ${HASHWORD} ];then
cat << EOF >> /etc/caddy/Caddyfile
${WEBTERMINAL_URL} {
basicauth {
birdnet ${HASHWORD}
handle /By_Date/* {
file_server browse
}
reverse_proxy localhost:8888
}
EOF
elif [ ! -z ${WEBTERMINAL_URL} ] && [ -z ${HASHWORD} ];then
cat << EOF >> /etc/caddy/Caddyfile
${WEBTERMINAL_URL} {
reverse_proxy localhost:8888
handle /Charts/* {
file_server browse
}
reverse_proxy /stream localhost:8000
php_fastcgi unix//run/php/php7.4-fpm.sock
reverse_proxy /log* localhost:8080
reverse_proxy /stats* localhost:8501
reverse_proxy /terminal* localhost:8888
}
EOF
fi
if [ ! -z ${BIRDNETLOG_URL} ];then
cat << EOF >> /etc/caddy/Caddyfile
${BIRDNETLOG_URL} {
reverse_proxy localhost:8080
}
EOF
fi
systemctl reload caddy
}
update_etc_hosts() {
sed -ie s/'$(hostname).local'/"$(hostname).local ${BIRDNETPI_URL//https:\/\/} ${WEBTERMINAL_URL//https:\/\/} ${BIRDNETLOG_URL//https:\/\/}"/g /etc/hosts
systemctl enable caddy
usermod -aG pi caddy
}
install_avahi_aliases() {
echo "Installing Avahi Services"
if ! which avahi-publish &> /dev/null; then
echo "Installing avahi-utils"
apt install -y avahi-utils &> /dev/null
fi
echo "Installing avahi-alias service"
cat << 'EOF' > /etc/systemd/system/avahi-alias@.service
cat << 'EOF' > /home/pi/BirdNET-Pi/templates/avahi-alias@.service
[Unit]
Description=Publish %I as alias for %H.local via mdns
After=network.target network-online.target
@@ -370,11 +271,30 @@ ExecStart=/bin/bash -c "/usr/bin/avahi-publish -a -R %I $(hostname -I |cut -d' '
[Install]
WantedBy=multi-user.target
EOF
systemctl enable avahi-alias@"$(hostname)".local.service
ln -sf /home/pi/BirdNET-Pi/templates/avahi-alias@.service /usr/lib/systemd/system
systemctl enable avahi-alias@"$(hostname)".local.service
}
install_birdnet_stats_service() {
cat << EOF > /home/pi/BirdNET-Pi/templates/birdnet_stats.service
[Unit]
Description=BirdNET Stats
[Service]
Restart=on-failure
RestartSec=5
Type=simple
User=${USER}
ExecStart=/home/pi/BirdNET-Pi/birdnet/bin/streamlit run /home/pi/BirdNET-Pi/scripts/plotly_streamlit.py --server.address localhost --server.baseUrlPath "/stats"
[Install]
WantedBy=multi-user.target
EOF
ln -sf /home/pi/BirdNET-Pi/templates/birdnet_stats.service /usr/lib/systemd/system
systemctl enable birdnet_stats.service
}
install_spectrogram_service() {
cat << EOF > /etc/systemd/system/spectrogram_viewer.service
cat << EOF > /home/pi/BirdNET-Pi/templates/spectrogram_viewer.service
[Unit]
Description=BirdNET-Pi Spectrogram Viewer
[Service]
@@ -386,12 +306,13 @@ ExecStart=/usr/local/bin/spectrogram.sh
[Install]
WantedBy=multi-user.target
EOF
systemctl enable spectrogram_viewer.service
ln -sf /home/pi/BirdNET-Pi/templates/spectrogram_viewer.service /usr/lib/systemd/system
systemctl enable spectrogram_viewer.service
}
install_chart_viewer_service() {
echo "Installing the chart_viewer.service"
cat << EOF > /etc/systemd/system/chart_viewer.service
cat << EOF > /home/pi/BirdNET-Pi/templates/chart_viewer.service
[Unit]
Description=BirdNET-Pi Chart Viewer Service
[Service]
@@ -403,21 +324,16 @@ ExecStart=/usr/local/bin/daily_plot.py
[Install]
WantedBy=multi-user.target
EOF
ln -sf /home/pi/BirdNET-Pi/templates/chart_viewer.service /usr/lib/systemd/system
systemctl enable chart_viewer.service
}
install_gotty_logs() {
echo "Installing GoTTY logging"
if ! which gotty &> /dev/null;then
echo "Installing GoTTY binary"
wget -c ${gotty_url} -O - | tar -xz -C /usr/local/bin/
fi
sudo -u ${USER} ln -sf $(dirname ${my_dir})/templates/gotty \
${HOME}/.gotty
sudo -u ${USER} ln -sf $(dirname ${my_dir})/templates/bashrc \
${HOME}/.bashrc
echo "Installing the birdnet_log.service"
cat << EOF > /etc/systemd/system/birdnet_log.service
cat << EOF > /home/pi/BirdNET-Pi/templates/birdnet_log.service
[Unit]
Description=BirdNET Analysis Log
[Service]
@@ -426,13 +342,13 @@ RestartSec=3
Type=simple
User=${USER}
Environment=TERM=xterm-256color
ExecStart=/usr/local/bin/gotty -p 8080 --title-format "BirdNET-Pi Log" birdnet_log.sh
ExecStart=/usr/local/bin/gotty --address localhost -p 8080 -P log --title-format "BirdNET-Pi Log" birdnet_log.sh
[Install]
WantedBy=multi-user.target
EOF
ln -sf /home/pi/BirdNET-Pi/templates/birdnet_log.service /usr/lib/systemd/system
systemctl enable birdnet_log.service
echo "Installing the web_terminal.service"
cat << EOF > /etc/systemd/system/web_terminal.service
cat << EOF > /home/pi/BirdNET-Pi/templates/web_terminal.service
[Unit]
Description=BirdNET-Pi Web Terminal
[Service]
@@ -441,62 +357,28 @@ RestartSec=3
Type=simple
User=${USER}
Environment=TERM=xterm-256color
ExecStart=/usr/local/bin/gotty -w -p 8888 --title-format "BirdNET-Pi Terminal" bash
ExecStart=/usr/local/bin/gotty --address localhost -w -p 8888 -P terminal --title-format "BirdNET-Pi Terminal" bash
[Install]
WantedBy=multi-user.target
EOF
ln -sf /home/pi/BirdNET-Pi/templates/web_terminal.service /usr/lib/systemd/system
systemctl enable web_terminal.service
}
install_sox() {
if which sox | grep mp3 &> /dev/null;then
echo "Sox is installed"
else
echo "Installing sox"
apt -qq update
apt install -y sox libsox-fmt-mp3
echo "Sox installed"
fi
}
install_php() {
if ! which php &> /dev/null || ! which php-fpm || ! apt list --installed | grep php-xml;then
echo "Installing PHP modules"
apt -qq update
apt install -qqy php php-fpm php-mysql php-xml php-zip
else
echo "PHP and PHP-FPM installed"
fi
echo "Configuring PHP for Caddy"
sed -i 's/www-data/caddy/g' /etc/php/*/fpm/pool.d/www.conf
systemctl restart php7\*-fpm.service
echo "Adding Caddy sudoers rule"
cat << EOF > /etc/sudoers.d/010_caddy-nopasswd
configure_caddy_php() {
echo "Configuring PHP for Caddy"
sed -i 's/www-data/caddy/g' /etc/php/*/fpm/pool.d/www.conf
systemctl restart php7\*-fpm.service
echo "Adding Caddy sudoers rule"
cat << EOF > /etc/sudoers.d/010_caddy-nopasswd
caddy ALL=(ALL) NOPASSWD: ALL
EOF
chmod 0440 /etc/sudoers.d/010_caddy-nopasswd
if [ ! -d ${HOME}/phpsysinfo ];then
echo "Fetching phpSysInfo"
sudo -u ${USER} git clone https://github.com/phpsysinfo/phpsysinfo.git \
${HOME}/phpsysinfo
fi
chmod 0440 /etc/sudoers.d/010_caddy-nopasswd
}
install_icecast() {
if ! which icecast2;then
echo "Installing IceCast2"
apt -qq update
echo "icecast2 icecast2/icecast-setup boolean false" | debconf-set-selections
apt install -qqy icecast2
config_icecast
systemctl enable icecast2.service
/etc/init.d/icecast2 start
else
echo "Icecast2 is installed"
config_icecast
systemctl enable icecast2.service
/etc/init.d/icecast2 start
fi
install_phpsysinfo() {
sudo -u ${USER} git clone https://github.com/phpsysinfo/phpsysinfo.git \
${HOME}/phpsysinfo
}
config_icecast() {
@@ -508,11 +390,11 @@ config_icecast() {
for i in "${passwords[@]}";do
sed -i "s/<${i}password>.*<\/${i}password>/<${i}password>${ICE_PWD}<\/${i}password>/g" /etc/icecast2/icecast.xml
done
systemctl enable icecast2.service
}
install_livestream_service() {
echo "Installing Live Stream service"
cat << EOF > /etc/systemd/system/livestream.service
cat << EOF > /home/pi/BirdNET-Pi/templates/livestream.service
[Unit]
Description=BirdNET-Pi Live Stream
After=network-online.target
@@ -527,72 +409,47 @@ ExecStart=/usr/local/bin/livestream.sh
[Install]
WantedBy=multi-user.target
EOF
ln -sf /home/pi/BirdNET-Pi/templates/livestream.service /usr/lib/systemd/system
systemctl enable livestream.service
}
install_nomachine() {
if [ ! -d /usr/share/NX ] && [ -d /etc/lightdm ];then
echo "Installing NoMachine"
curl -s -o ${HOME}/nomachine.deb -O "${nomachine_url}"
apt install -y ${HOME}/nomachine.deb
rm -f ${HOME}/nomachine.deb
echo "Enabling VNC"
systemctl enable --now vncserver-x11-serviced.service
fi
}
install_cleanup_cron() {
echo "Installing the cleanup.cron"
cat $(dirname ${my_dir})/templates/cleanup.cron >> /etc/crontab
}
install_selected_services() {
install_services() {
set_hostname
update_system
update_etc_hosts
set_login
install_depends
install_scripts
install_Caddyfile
install_avahi_aliases
install_birdnet_analysis
install_birdnet_server
if [[ "${DO_EXTRACTIONS}" =~ [Yy] ]];then
install_extraction_service
fi
if [[ "${DO_RECORDING}" =~ [Yy] ]];then
install_alsa
install_recording_service
fi
install_php
install_caddy
install_Caddyfile
update_etc_hosts
install_avahi_aliases
install_gotty_logs
install_sox
install_mariadb
install_spectrogram_service
install_chart_viewer_service
install_pushed_notifications
if [ ! -z "${ICE_PWD}" ];then
install_icecast
install_livestream_service
fi
if [[ "${INSTALL_NOMACHINE}" =~ [Yy] ]];then
install_nomachine
fi
install_birdnet_stats_service
install_recording_service
install_custom_recording_service # But does not enable
install_extraction_service
install_pushed_notifications
install_spectrogram_service
install_chart_viewer_service
install_gotty_logs
install_phpsysinfo
install_livestream_service
install_cleanup_cron
create_necessary_dirs
generate_BirdDB
install_cleanup_cron
install_ftpd
install_lynx
configure_caddy_php
config_icecast
${my_dir}/createdb.sh
}
if [ -f ${config_file} ];then
source ${config_file}
install_selected_services
install_services
else
echo "Unable to find a configuration file. Please make sure that $config_file exists."
fi
+105 -107
View File
@@ -5,133 +5,131 @@ error_reporting(E_ALL);
header("refresh: 300;");
$myDate = date('Y-m-d');
$chart = "Combo-$myDate.png";
$mysqli = mysqli_connect();
$mysqli->select_db('birds');
if ($mysqli->connect_error) {
die('Connect Error (' .
$mysqli->connect_errno . ') '.
$mysqli->connect_error);
$db = new SQLite3('/home/pi/BirdNET-Pi/scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
if($db == False) {
echo "Database is busy";
header("refresh: 0;");
}
// SQL query to select data from database
$sql0 = "SELECT COUNT(*) AS 'Total' FROM detections";
$totalcount = $mysqli->query($sql0);
$statement = $db->prepare('SELECT COUNT(*) FROM detections');
if($statement == False) {
echo "Database is busy";
header("refresh: 0;");
}
$result = $statement->execute();
$totalcount = $result->fetchArray(SQLITE3_ASSOC);
$sql1 = "SELECT Com_Name, Sci_Name, Date, Time FROM detections
ORDER BY Date DESC, Time DESC LIMIT 1";
$mostrecent = $mysqli->query($sql1);
$statement2 = $db->prepare('SELECT COUNT(*) FROM detections WHERE Date == DATE(\'now\', \'localtime\')');
if($statement2 == False) {
echo "Database is busy";
header("refresh: 0;");
}
$result2 = $statement2->execute();
$todaycount = $result2->fetchArray(SQLITE3_ASSOC);
$sql2 = "SELECT COUNT(*) AS 'Total' FROM detections
WHERE Date = CURDATE()";
$todayscount = $mysqli->query($sql2);
$statement3 = $db->prepare('SELECT COUNT(*) FROM detections WHERE Date == Date(\'now\', \'localtime\') AND TIME >= TIME(\'now\', \'localtime\', \'-1 hour\')');
if($statement3 == False) {
echo "Database is busy";
header("refresh: 0;");
}
$result3 = $statement3->execute();
$hourcount = $result3->fetchArray(SQLITE3_ASSOC);
$sql3 = "SELECT COUNT(*) AS 'Total' FROM detections
WHERE Date = CURDATE()
AND Time >= DATE_SUB(NOW(),INTERVAL 1 HOUR)";
$lasthourcount = $mysqli->query($sql3);
$statement4 = $db->prepare('SELECT Com_Name, Sci_Name, Date, Time, Confidence, File_Name FROM detections ORDER BY Date DESC, Time DESC LIMIT 1');
if($statement4 == False) {
echo "Database is busy";
header("refresh: 0;");
}
$result4 = $statement4->execute();
$mostrecent = $result4->fetchArray(SQLITE3_ASSOC);
$comname = preg_replace('/ /', '_', $mostrecent['Com_Name']);
$sciname = preg_replace('/ /', '_', $mostrecent['Sci_Name']);
$comname = preg_replace('/\'/', '', $comname);
$filename = "/By_Date/".$mostrecent['Date']."/".$comname."/".$mostrecent['File_Name'];
$sql4 = "SELECT Com_Name
FROM detections
WHERE Date = CURDATE()
GROUP BY Com_Name";
$specieslist = $mysqli->query($sql4);
$speciescount = mysqli_num_rows($specieslist);
$statement5 = $db->prepare('SELECT COUNT(DISTINCT(Com_Name)) FROM detections WHERE Date == Date(\'now\',\'localtime\')');
if($statement5 == False) {
echo "Database is busy";
header("refresh: 0;");
}
$result5 = $statement5->execute();
$speciestally = $result5->fetchArray(SQLITE3_ASSOC);
$mysqli->close();
$statement6 = $db->prepare('SELECT COUNT(DISTINCT(Com_Name)) FROM detections');
if($statement6 == False) {
echo "Database is busy";
header("refresh: 0;");
}
$result6 = $statement6->execute();
$totalspeciestally = $result6->fetchArray(SQLITE3_ASSOC);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Overview</title>
<!-- CSS FOR STYLING THE PAGE -->
<link rel="stylesheet" href="style.css">
<style>
a {
text-decoration: none;
color:black;
}
.center {
display: block;
margin-left: 5px;
margin-right: 5px;
width: 90%;
padding: 5px;
}
.center2 {
display: block;
margin-left: 5px;
margin-right: 5px;
width: 100%;
padding: 5px;
body::-webkit-scrollbar {
display:none
}
</style>
</head>
<body style="background-color: rgb(119, 196, 135);">
<h2>Overview</h2>
<div class="row">
<div class="column2">
<?php // LOOP TILL END OF DATA
while($rows=$mostrecent ->fetch_assoc())
{
$dbname = preg_replace('/ /', '_', $rows['Com_Name']);
$dbname = preg_replace('/\'/', '', $dbname);
$dbsciname = preg_replace('/ /', '_', $rows['Sci_Name']);
?>
<table>
<tr>
<th>Most Recent Detection</th>
<td><a href="/By_Common_Name/<?php echo $dbname;?>"><?php echo $rows['Com_Name'];?></a></td>
<td><a href="/By_Date/<?php echo $rows['Date'];?>"/><?php echo $rows['Date'];?></a></td>
<td><?php echo $rows['Time'];?></td>
<td><a href="https://wikipedia.org/wiki/<?php echo $dbsciname;?>" target="top"/>More Info</a></td>
</tr>
</table>
</div>
</div>
<?php
}
?>
<div class="row">
<div class="column">
<table>
<tr>
<th></th>
<th>Total</th>
<th>Today</th>
<th>Last Hour</th>
</tr>
<tr>
<th>Number of Detections</th>
<td><?php while ($row = $totalcount->fetch_assoc()) { echo $row['Total']; };?></td>
<td><?php while ($row = $todayscount->fetch_assoc()) { echo $row['Total']; };?></td>
<td><?php while ($row = $lasthourcount->fetch_assoc()) { echo $row['Total']; };?></td>
</tr>
</table>
</div>
<div class="column">
<table>
<tr>
<th>Species Detected Today</th>
<td><?php echo $speciescount;?></td>
</tr>
</table>
</div>
<div class="overview">
<div class="overview-stats">
<div class="left-column">
<table>
<tr>
<th>Total</th>
<td><?php echo $totalcount['COUNT(*)'];?></td>
</tr>
<tr>
<th>Today</th>
<form action="" method="POST">
<td><input type="hidden" name="view" value="Recordings"><button type="submit" name="date" value="<?php echo date('Y-m-d');?>"><?php echo $todaycount['COUNT(*)'];?></button></td>
</form>
</tr>
<tr>
<th>Last Hour</th>
<td><?php echo $hourcount['COUNT(*)'];?></td>
</tr>
<tr>
<th>Species Detected Today</th>
<form action="" method="POST">
<td><button type="submit" name="view" value="Species Stats"><?php echo $speciestally['COUNT(DISTINCT(Com_Name))'];?></button></td>
</form>
</tr>
<tr>
<th>Total Number of Species</th>
<form action="" method="POST">
<td><button type="submit" name="view" value="Species Stats"><?php echo $totalspeciestally['COUNT(DISTINCT(Com_Name))'];?></button></td>
</form>
</tr>
</table>
</div>
<div class="right-column">
<div class="chart">
<?php
if (file_exists('/home/pi/BirdSongs/Extracted/Charts/'.$chart)) {
echo "<img src=\"/Charts/$chart?nocache=time()\" style=\"width: 100%;padding: 5px;margin-left: auto;margin-right: auto;display: block;\">";
echo "<img src=\"/Charts/$chart?nocache=time()\">";
} else {
echo "<p style=\"text-align:center;margin-left:-150px;\">No Detections For Today</p>";
echo "<p>No Detections For Today</p>";
}
?>
<h2>Currently Analyzing</h2>
<img src='/spectrogram.png?nocache=<?php echo time();?>' style="width: 100%;padding: 5px;">
</html>
</div>
<table>
<h3>Most Recent Detection: <span style="font-weight: normal;"><?php echo $mostrecent['Date']." ".$mostrecent['Time'];?></span></h3>
<tr>
<td>
<form action="" method="POST">
<input type="hidden" name="view" value="Species Stats">
<button type="submit" name="species" value="<?php echo $mostrecent['Com_Name'];?>"><?php echo $mostrecent['Com_Name'];?>: </button>
<a href="https://wikipedia.org/wiki/<?php echo $sciname;?>" target="_blank"/><i><?php echo $mostrecent['Sci_Name'];?></i></a>
<br>Confidence: <?php echo $mostrecent['Confidence'];?><br>
<video controls poster="<?php echo $filename.".png";?>" preload="none" title="<?php echo $filename;?>"><source src="<?php echo $filename;?>"></video></td>
</form>
</tr>
</table>
<h3>Currently Analyzing</h3>
<img src='/spectrogram.png?nocache=<?php echo time();?>' >
</div>
</div>
+153
View File
@@ -0,0 +1,153 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$db = new SQLite3('/home/pi/BirdNET-Pi/scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
if($db == False){
echo "Database is busy";
header("refresh: 0;");
}
#By Date
if(isset($_POST['bydate'])){
$statement = $db->prepare('SELECT DISTINCT(Date) FROM detections GROUP BY Date');
if($statement == False){
echo "Database is busy";
header("refresh: 0;");
}
$result = $statement->execute();
$view = "bydate";
#Specific Date
} elseif(isset($_POST['date'])) {
$date = $_POST['date'];
session_start();
$_SESSION['date'] = $date;
$statement = $db->prepare("SELECT DISTINCT(Com_Name) FROM detections WHERE Date == \"$date\" ORDER BY Com_Name");
if($statement == False){
echo "Database is busy";
header("refresh: 0;");
}
$result = $statement->execute();
$view = "date";
#By Species
} elseif(isset($_POST['byspecies'])) {
$statement = $db->prepare('SELECT DISTINCT(Com_Name) FROM detections ORDER BY Com_Name');
session_start();
if($statement == False){
echo "Database is busy";
header("refresh: 0;");
}
$result = $statement->execute();
$view = "byspecies";
#Specific Species
} elseif(isset($_POST['species'])) {
$species = $_POST['species'];
session_start();
$_SESSION['species'] = $species;
$statement = $db->prepare("SELECT * FROM detections WHERE Com_Name == \"$species\" ORDER BY Com_Name");
$statement3 = $db->prepare("SELECT Date, Time, Sci_Name, MAX(Confidence), File_Name FROM detections WHERE Com_Name == \"$species\" ORDER BY Com_Name");
if($statement == False || $statement3 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result = $statement->execute();
$result3 = $statement3->execute();
$view = "species";
} else {
session_start();
session_unset();
$view = "choose";
}
?>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
</style>
</head>
<?php
#If no specific species
if(!isset($_POST['species'])){
?>
<div class="play">
<table>
<tr>
<form action="" method="POST">
<input type="hidden" name="view" value="Recordings">
<?php
#By Date
if($view == "bydate") {
while($results=$result->fetchArray(SQLITE3_ASSOC)){
$date = $results['Date'];
echo "<td>
<button action=\"submit\" name=\"date\" value=\"$date\">$date</button></td></tr>";}
#By Species
} elseif($view == "byspecies") {
while($results=$result->fetchArray(SQLITE3_ASSOC)){
$name = $results['Com_Name'];
echo "<td>
<button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>";}
#Specific Date
} elseif($view == "date") {
while($results=$result->fetchArray(SQLITE3_ASSOC)){
$name = $results['Com_Name'];
echo "<td>
<button action=\"submit\" name=\"species\" value=\"$name\">$name</button></td></tr>";}
#Choose
} else {
echo "<td>
<button action=\"submit\" name=\"byspecies\" value=\"byspecies\">By Species</button></td></tr>
<tr><td><button action=\"submit\" name=\"bydate\" value=\"bydate\">By Date</button></td>";
}
echo "</form>
</tr>
</table>";
}
#Specific Species
if(isset($_POST['species'])){
$name = $_POST['species'];
if(isset($_SESSION['date'])) {
$date = $_SESSION['date'];
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" AND Date == \"$date\" ORDER BY Time DESC");
} else {
$statement2 = $db->prepare("SELECT * FROM detections where Com_Name == \"$name\" ORDER BY Date DESC, Time DESC");}
if($statement2 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result2 = $statement2->execute();
echo "<table>
<tr>
<th>$name</th>
</tr>";
while($results=$result2->fetchArray(SQLITE3_ASSOC))
{
$comname = preg_replace('/ /', '_', $results['Com_Name']);
$comname = preg_replace('/\'/', '', $comname);
$date = $results['Date'];
$filename = "/By_Date/".$date."/".$comname."/".$results['File_Name'];
$sciname = preg_replace('/ /', '_', $results['Sci_Name']);
$sci_name = $results['Sci_Name'];
$time = $results['Time'];
$confidence = $results['Confidence'];
echo "<tr>
<td>$date $time<br>$confidence<br>
<video controls poster=\"$filename.png\" preload=\"none\" title=\"$filename\"><source src=\"$filename\"></video></td>
</tr>";
}echo "</table>";}?>
</div>
</html>
+184
View File
@@ -0,0 +1,184 @@
#!/home/pi/BirdNET-Pi/birdnet/bin/python3
import streamlit as st
import pandas as pd
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from datetime import timedelta, datetime
from pathlib import Path
import sqlite3
from sqlite3 import Connection
URI_SQLITE_DB = "/home/pi/BirdNET-Pi/scripts/birds.db"
st.set_page_config(layout='wide')
# Remove whitespace from the top of the page and sidebar
st.markdown("""
<style>
.css-18e3th9 {
padding-top: 2.5rem;
padding-bottom: 10rem;
padding-left: 5rem;
padding-right: 5rem;
}
.css-1d391kg {
padding-top: 3.5rem;
padding-right: 1rem;
padding-bottom: 3.5rem;
padding-left: 1rem;
}
</style>
""", unsafe_allow_html=True)
col1,col2,col3 = st.columns([20,20,20])
col1.title('BirdNET-Pi', anchor=None)
col2.image('/home/pi/BirdNET-Pi/homepage/images/bird.png')
col3.text('')
@st.cache(hash_funcs={Connection: id})
def get_connection(path:str):
return sqlite3.connect(path,check_same_thread=False)
# def load_data():
# df1 = pd.read_csv('/home/pi/BirdNET-Pi/BirdDB.txt', sep=';')
# return df1
def get_data(conn: Connection):
df1=pd.read_sql("SELECT * FROM detections", con=conn)
return df1
conn = get_connection(URI_SQLITE_DB)
# Read in the cereal data
# df = load_data()
df=get_data(conn)
df2=df.copy()
df2['DateTime']=pd.to_datetime(df2['Date'] + " " + df2['Time'])
df2=df2.set_index('DateTime')
# Filter on date range
# Date as calendars
# Start_Date = pd.to_datetime(st.sidebar.date_input('Which date do you want to start?', value = df2.index.min()))
# End_Date = pd.to_datetime(st.sidebar.date_input('Which date do you want to end?', value = df2.index.max()))
# Date as slider
Start_Date = pd.to_datetime(df2.index.min()).date()
End_Date = pd.to_datetime(df2.index.max()).date()
Date_Slider = st.slider('Date Range',
min_value = Start_Date-timedelta(days=1),
max_value = End_Date,
value=(Start_Date,
End_Date)
)
filt = (df2.index >= pd.Timestamp(Date_Slider[0])) & (df2.index <= pd.Timestamp(Date_Slider[1]+timedelta(days=1)))
df2 = df2[filt]
#Create species count for selected date range
Specie_Count=df2['Com_Name'].value_counts()
#Create species treemap
# Create Hourly Crosstab
hourly=pd.crosstab(df2['Com_Name'],df2.index.hour, dropna=False)
# Filter on species
species = list(hourly.index)
cols1,cols2= st.columns((1,1))
top_N = cols1.slider(
'Select Number of Birds to Show',
min_value = 1,
value=min(10,len(Specie_Count))
)
top_N_species = (df2['Com_Name'].value_counts()[:top_N])
specie = cols2.selectbox('Which bird would you like to explore for the dates '+str(Date_Slider[0])+' to '+str(Date_Slider[1])+'?', species,
index=species.index(list(top_N_species.index)[0]))
font_size=15
#specie filter
filt=df2['Com_Name']==specie
df_counts=df2[filt].resample('D').count()
fig = make_subplots(
rows=3, cols =2,
specs= [[{"type":"xy","rowspan":3}, {"type":"polar","rowspan":2}], [{"rowspan":1}, {"rowspan":1} ], [None, {"type":"xy","rowspan":1}]],
subplot_titles=('<b>Top '+ str(top_N) + ' Species in Date Range '+str(Date_Slider[0])+' to '+str(Date_Slider[1])+'</b>',
'Total Detect:'+str('{:,}'.format(sum(df_counts.Time)))+
' Confidence Max:'+str('{:.2f}%'.format(max(df2[df2['Com_Name']==specie]['Confidence'])*100))+
' '+' Median:'+str('{:.2f}%'.format(np.median(df2[df2['Com_Name']==specie]['Confidence'])*100))
)
)
fig.layout.annotations[1].update(x=0.7,y=0.25, font_size=15)
#Plot seen species for selected date range and number of species
fig.add_trace(go.Bar(y=top_N_species.index, x=top_N_species, orientation='h'), row=1,col=1)
fig.update_layout(
margin=dict(l=0, r=0, t=50, b=0),
yaxis={'categoryorder':'total ascending'})
# Set 360 degrees, 24 hours for polar plot
theta = np.linspace(0.0, 360, 24, endpoint=False)
d=pd.DataFrame(np.zeros((23,1))).squeeze()
detections = hourly.loc[specie]
detections=(d+detections).fillna(0)
fig.add_trace(go.Barpolar(r = detections, theta=theta), row=1, col=2)
fig.update_layout(
autosize=False,
width = 1000,
height = 500,
showlegend=False,
polar = dict(
radialaxis = dict(
tickfont_size = font_size,
showticklabels = True,
hoverformat = "#%{theta}: <br>Popularity: %{percent} </br> %{r}"
),
angularaxis = dict(
tickfont_size= font_size,
rotation = -90,
direction = 'clockwise',
tickmode='array',
tickvals=[0,15,35,45,60,75,90,105,120,135,150,165,180,195,210,225,240,255,270,285,300,315,330,345],
ticktext=['12am','1am','2am','3am','4am','5am', '6am','7am','8am','9am','10am','11am','12pm','1pm','2pm','3pm','4pm','5pm', '6pm','7pm','8pm','9pm','10pm','11pm'],
hoverformat = "#%{theta}: <br>Popularity: %{percent} </br> %{r}"
),
),
)
daily=pd.crosstab(df2['Com_Name'],df2.index.date, dropna=False)
fig.add_trace(go.Bar(x=daily.columns, y=daily.loc[specie]), row=3, col=2)
# container=st.container()
# config={'displayModelBar': False}
st.plotly_chart(fig, use_container_width=True) #, config=config)
# cols3,cols4=st.columns((1,1))
#
# extract_date=Date_Slider
#
# audio_file = open('/home/pi/BirdSongs/Extracted/By_Date/2022-03-22/Yellow-streaked_Greenbul/Yellow-streaked_Greenbul-77-2022-03-22-birdnet-15:04:28.mp3', 'rb')
# audio_bytes = audio_file.read()
# cols4.audio(audio_bytes, format='audio/mp3')
-9
View File
@@ -1,9 +0,0 @@
#!/usr/bin/env bash
# Rebuild DB from BirdDB.txt
source /etc/birdnet/birdnet.conf
BIRDNET_DIR=/home/pi/BirdNET-Pi
sudo ${BIRDNET_DIR}/scripts/createdb_bullseye.sh
sudo mysql birds -e "
LOAD DATA LOCAL INFILE '/home/pi/BirdNET-Pi/BirdDB.txt'
INTO TABLE detections
FIELDS TERMINATED BY ';'"
+1 -2
View File
@@ -13,7 +13,6 @@ spectrogram_viewer.service
pushed_notifications.service
livestream.service
icecast2.service
extraction.timer
extraction.service
chart_viewer.service
birdnet_recording.service
@@ -24,6 +23,6 @@ sudo systemctl restart "${i}"
done
until grep 5050 <(netstat -tulpn 2>&1);do
sudo systemctl restart birdnet_server.service
sleep 30
sleep 45
done
sudo systemctl restart birdnet_analysis.service
-43
View File
@@ -1,43 +0,0 @@
#!/usr/bin/bash
# Writes variables to config file
set -x
birdnetpi_dir=/home/pi/BirdNET-Pi
birdnet_conf=${birdnetpi_dir}/birdnet.conf
if ! [ -z ${new_lat} ];then
sed -i s/'^LATITUDE=.*'/"LATITUDE=${new_lat}"/g ${birdnet_conf}
fi
if ! [ -z ${new_lon} ];then
sed -i s/'^LONGITUDE=.*'/"LONGITUDE=${new_lon}"/g ${birdnet_conf}
fi
if ! [ -z ${caddy_pwd} ];then
sed -i s/'^CADDY_PWD=.*'/"CADDY_PWD=${caddy_pwd}"/g ${birdnet_conf}
hash_pwd=$(caddy hash-password -plaintext ${caddy_pwd})
sudo sed -i s/'birdnet\ .*'/"birdnet ${hash_pwd}"/g /etc/caddy/Caddyfile
sudo systemctl reload caddy
fi
if ! [ -z ${db_pwd} ];then
sed -i s/'^DB_PWD=.*'/"DB_PWD=${db_pwd}"/g ${birdnet_conf}
${birdnetpi_dir}/scripts/update_db_pwd_bullseye.sh
fi
if ! [ -z ${birdweather_id} ];then
sed -i s/'^BIRDWEATHER_ID=.*'/"BIRDWEATHER_ID=${birdweather_id}"/g ${birdnet_conf}
fi
if ! [ -z ${birdnetpi_url} ];then
sed -i s/'^BIRDNETPI_URL=.*'/"BIRDNETPI_URL=${birdnetpi_url/\/\//\\\/\\\/}"/g ${birdnet_conf}
sed -i s/'^WEBTERMINAL_URL=.*'/"WEBTERMINAL_URL=${extractionlog_url/\/\//\\\/\\\/}"/g ${birdnet_conf}
sed -i s/'^BIRDNETLOG_URL=.*'/"BIRDNETLOG_URL=${birdnetlog_url/\/\//\\\/\\\/}"/g ${birdnet_conf}
fi
if ! [ -z ${new_sensitivity} ];then
sed -i s/'^SENSITIVITY=.*'/"SENSITIVITY=${new_sensitivity}"/g ${birdnet_conf}
fi
if ! [ -z ${new_confidence} ];then
sed -i s/'^CONFIDENCE=.*'/"CONFIDENCE=${new_confidence}"/g ${birdnet_conf}
fi
+68 -75
View File
@@ -18,10 +18,8 @@ import math
import time
from decimal import Decimal
import json
###############################################################################
import requests
import mysql.connector
###############################################################################
import sqlite3
import datetime
from time import sleep
import pytz
@@ -48,7 +46,7 @@ except:
# Open most recent Configuration and grab DB_PWD as a python variable
with open('/home/pi/BirdNET-Pi/thisrun.txt', 'r') as f:
this_run = f.readlines()
db_pwd = str(str(str([i for i in this_run if i.startswith('DB_PWD')]).split('=')[1]).split('\\')[0])
audiofmt = "." + str(str(str([i for i in this_run if i.startswith('AUDIOFMT')]).split('=')[1]).split('\\')[0])
def loadModel():
@@ -304,7 +302,7 @@ def handle_client(conn, addr):
#print('Time:', date_time_obj.time())
print('Date-time:', date_time_obj)
now = date_time_obj
current_date = now.strftime("%Y/%m/%d")
current_date = now.strftime("%Y-%m-%d")
current_time = now.strftime("%H:%M:%S")
current_iso8601 = now.astimezone(get_localzone()).isoformat()
@@ -331,88 +329,83 @@ def handle_client(conn, addr):
myReturn += str(i) + '-' + str(detections[i][0]) + '\n'
with open('/home/pi/BirdNET-Pi/BirdDB.txt', 'a') as rfile:
for d in detections:
for entry in detections[d]:
if entry[1] >= min_conf and ((entry[0] in INCLUDE_LIST or len(INCLUDE_LIST) == 0) and (entry[0] not in EXCLUDE_LIST or len(EXCLUDE_LIST) == 0) ):
rfile.write(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' \
+ str(entry[1]) +";" + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' \
+ str(sensitivity) +';' + str(args.overlap) + '\n')
def insert_variables_into_table(Date, Time, Sci_Name, Com_Name, Confidence, Lat, Lon, Cutoff, Week, Sens, Overlap):
try:
connection = mysql.connector.connect(host='localhost',
database='birds',
user='birder',
password=db_pwd)
cursor = connection.cursor()
mySql_insert_query = """INSERT INTO detections (Date, Time, Sci_Name, Com_Name, Confidence, Lat, Lon, Cutoff, Week, Sens, Overlap)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """
+ str(args.sensitivity) +';' + str(args.overlap) + '\n')
record = (Date, Time, Sci_Name, Com_Name, Confidence, Lat, Lon, Cutoff, Week, Sens, Overlap)
cursor.execute(mySql_insert_query, record)
connection.commit()
print("Record inserted successfully into detections table")
except mysql.connector.Error as error:
print("Failed to insert record into detections table {}".format(error))
finally:
if connection.is_connected():
connection.close()
print("MySQL connection is closed")
Date = str(current_date)
Time = str(current_time)
species = entry[0]
sci_name,com_name = species.split('_')
insert_variables_into_table(str(current_date), str(current_time), sci_name, com_name, \
str(entry[1]), str(args.lat), str(args.lon), str(min_conf), str(week), \
str(args.sensitivity), str(args.overlap))
Sci_Name,Com_Name = species.split('_')
score = entry[1]
Confidence = str(round(score*100))
Lat = str(args.lat)
Lon = str(args.lon)
Cutoff = str(args.min_conf)
Week = str(args.week)
Sens = str(args.sensitivity)
Overlap = str(args.overlap)
Com_Name = Com_Name.replace("'", "")
File_Name = Com_Name.replace(" ", "_") + '-' + Confidence + '-' + \
Date.replace("/", "-") + '-birdnet-' + Time + audiofmt
print(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) +";" + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' + str(args.sensitivity) +';' + str(args.overlap) + '\n')
#Connect to SQLite Database
try:
con = sqlite3.connect('/home/pi/BirdNET-Pi/scripts/birds.db')
cur = con.cursor()
cur.execute("INSERT INTO detections VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (Date, Time, Sci_Name, Com_Name, str(score), Lat, Lon, Cutoff, Week, Sens, Overlap, File_Name))
con.commit()
con.close()
except:
print("Database busy")
time.sleep(2)
print(str(current_date) + ';' + str(current_time) + ';' + entry[0].replace('_', ';') + ';' + str(entry[1]) + ';' + str(args.lat) + ';' + str(args.lon) + ';' + str(min_conf) + ';' + str(week) + ';' + str(args.sensitivity) +';' + str(args.overlap) + Com_Name.replace(" ", "_") + '-' + str(score) + '-' + str(current_date) + '-birdnet-' + str(current_time) + audiofmt + '\n')
if birdweather_id != "99999":
try:
if soundscape_uploaded is False:
# POST soundscape to server
soundscape_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/soundscapes" + "?timestamp=" + current_iso8601
with open(args.i, 'rb') as f:
wav_data = f.read()
response = requests.post(url=soundscape_url, data=wav_data, headers={'Content-Type': 'application/octet-stream'})
print("Soundscape POST Response Status - ", response.status_code)
sdata = response.json()
soundscape_id = sdata['soundscape']['id']
soundscape_uploaded = True
# POST detection to server
detection_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/detections"
start_time = d.split(';')[0]
end_time = d.split(';')[1]
post_begin = "{ "
now_p_start = now + datetime.timedelta(seconds=float(start_time))
current_iso8601 = now_p_start.astimezone(get_localzone()).isoformat()
post_timestamp = "\"timestamp\": \"" + current_iso8601 + "\","
post_lat = "\"lat\": " + str(args.lat) + ","
post_lon = "\"lon\": " + str(args.lon) + ","
post_soundscape_id = "\"soundscapeId\": " + str(soundscape_id) + ","
post_soundscape_start_time = "\"soundscapeStartTime\": " + start_time + ","
post_soundscape_end_time = "\"soundscapeEndTime\": " + end_time + ","
post_commonName = "\"commonName\": \"" + entry[0].split('_')[1] + "\","
post_scientificName = "\"scientificName\": \"" + entry[0].split('_')[0] + "\","
post_algorithm = "\"algorithm\": " + "\"alpha\"" + ","
post_confidence = "\"confidence\": " + str(entry[1])
post_end = " }"
post_json = post_begin + post_timestamp + post_lat + post_lon + post_soundscape_id + post_soundscape_start_time + post_soundscape_end_time + post_commonName + post_scientificName + post_algorithm + post_confidence + post_end
print(post_json)
response = requests.post(detection_url, json=json.loads(post_json))
print("Detection POST Response Status - ", response.status_code)
if soundscape_uploaded is False:
# POST soundscape to server
soundscape_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/soundscapes" + "?timestamp=" + current_iso8601
with open(args.i, 'rb') as f:
wav_data = f.read()
response = requests.post(url=soundscape_url, data=wav_data, headers={'Content-Type': 'application/octet-stream'})
print("Soundscape POST Response Status - ", response.status_code)
sdata = response.json()
soundscape_id = sdata['soundscape']['id']
soundscape_uploaded = True
# POST detection to server
detection_url = "https://app.birdweather.com/api/v1/stations/" + birdweather_id + "/detections"
start_time = d.split(';')[0]
end_time = d.split(';')[1]
post_begin = "{ "
now_p_start = now + datetime.timedelta(seconds=float(start_time))
current_iso8601 = now_p_start.astimezone(get_localzone()).isoformat()
post_timestamp = "\"timestamp\": \"" + current_iso8601 + "\","
post_lat = "\"lat\": " + str(args.lat) + ","
post_lon = "\"lon\": " + str(args.lon) + ","
post_soundscape_id = "\"soundscapeId\": " + str(soundscape_id) + ","
post_soundscape_start_time = "\"soundscapeStartTime\": " + start_time + ","
post_soundscape_end_time = "\"soundscapeEndTime\": " + end_time + ","
post_commonName = "\"commonName\": \"" + entry[0].split('_')[1] + "\","
post_scientificName = "\"scientificName\": \"" + entry[0].split('_')[0] + "\","
post_algorithm = "\"algorithm\": " + "\"alpha\"" + ","
post_confidence = "\"confidence\": " + str(entry[1])
post_end = " }"
post_json = post_begin + post_timestamp + post_lat + post_lon + post_soundscape_id + post_soundscape_start_time + post_soundscape_end_time + post_commonName + post_scientificName + post_algorithm + post_confidence + post_end
print(post_json)
response = requests.post(detection_url, json=json.loads(post_json))
print("Detection POST Response Status - ", response.status_code)
except:
print("Cannot POST right now")
conn.send(myReturn.encode(FORMAT))
#time.sleep(3)
+85 -106
View File
@@ -1,107 +1,86 @@
<html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
font-family: 'Arial', 'Gill Sans', 'Gill Sans MT',
' Calibri', 'Trebuchet MS', 'sans-serif';
box-sizing: border-box;
box-sizing: border-box;
}
/* Create two unequal columns that floats next to each other */
.column {
float: left;
padding: 10px;
}
.first {
width: calc(50% - 70px);
}
.second {
width: calc(50% - 30px);
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
body {
background-color: rgb(119, 196, 135);
}
a {
text-decoration: none;
color: white;
}
.block {
display: block;
font-weight: bold;
width:100%;
border: none;
background-color: #04AA6D;
padding: 20px 20px;
color: white;
font-size: medium;
cursor: pointer;
text-align: center;
}
@media screen and (max-width: 800px) {
.column {
float: none;
width: 100%;
}
}
</style>
<body style="background-color: rgb(119, 196, 135);">
<div class="row">
<div class="column first">
<form action="" method="POST" onclick="return confirm('Stop core services?')">
<input type="hidden" name="submit" value="stop_core_services.sh">
<button type="submit" class="block">Stop Core Services</button>
</form>
<form action="" method="POST" onclick="return confirm('Restart ALL services?')">
<input type="hidden" name="submit" value="restart_services.sh">
<button type="submit" class="block">Restart ALL Services</button>
</form>
<form action="" method="POST">
<input type="hidden" name="submit" value="sudo systemctl restart birdnet_analysis.service">
<button type="submit" class="block">Restart BirdNET Analysis</button>
</form>
<form action="" method="POST">
<input type="hidden" name="submit" value="sudo systemctl restart birdnet_recording.service">
<button type="submit" class="block">Restart Recording</button>
</form>
<form action="" method="POST" onclick="return confirm('Restart Caddy? You will be disconnected for about 20 seconds.')">
<input type="hidden" name="submit" value="sudo systemctl restart caddy">
<button type="submit" class="block">Restart Caddy</button>
</form>
</div>
<div class="column second">
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if(isset($_POST['submit'])){
$command = $_POST['submit'];
if($command == 'restart_services.sh'){
$str= "<h3>Restarting Services</h3>
<p>Please wait 60-90 seconds</p>";
echo str_pad($str, 4096);
ob_flush();
flush();
}
if(isset($command)){
$results = shell_exec("$command 2>&1");
echo "<pre>$results</pre>";
}
}
ob_end_flush();
?>
</div>
</div>
</body>
<br>
<br>
<div class="servicecontrols">
<form action="" method="POST">
<h3>Live Audio Stream</h3>
<button type="submit" name="submit" value="sudo systemctl stop livestream.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart livestream.service">Restart </button>
<button type="submit" name="submit" value="sudo systemctl disable --now livestream.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now livestream.service">Enable</button>
</form>
<form action="" method="POST">
<h3>Web Terminal</h3>
<button type="submit" name="submit" value="sudo systemctl stop web_terminal.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart web_terminal.service">Restart </button>
<button type="submit" name="submit" value="sudo systemctl disable --now web_terminal.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now web_terminal.service">Enable</button>
</form>
<form action="" method="POST">
<h3>BirdNET Log</h3>
<button type="submit" name="submit" value="sudo systemctl stop birdnet_log.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart birdnet_log.service">Restart </button>
<button type="submit" name="submit" value="sudo systemctl disable --now birdnet_log.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_log.service">Enable</button>
</form>
<form action="" method="POST">
<h3>Extraction Service</h3>
<button type="submit" name="submit" value="sudo systemctl stop extraction.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart extraction.service">Restart </button>
<button type="submit" name="submit" value="sudo systemctl disable --now extraction.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now extraction.service">Enable</button>
</form>
<form action="" method="POST">
<h3>BirdNET Analysis Server</h3>
<button type="submit" name="submit" value="sudo systemctl stop birdnet_server.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart birdnet_server.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now birdnet_server.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_server.service">Enable</button>
</form>
<form action="" method="POST">
<h3>BirdNET Analysis Client</h3>
<button type="submit" name="submit" value="sudo systemctl stop birdnet_analysis.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart birdnet_analysis.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now birdnet_analysis.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_analysis.service">Enable</button>
</form>
<form action="" method="POST">
<h3>Streamlit Statistics</h3>
<button type="submit" name="submit" value="sudo systemctl stop birdnet_stats.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart birdnet_stats.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now birdnet_stats.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_stats.service">Enable</button>
</form>
<form action="" method="POST">
<h3>Recording Service</h3>
<button type="submit" name="submit" value="sudo systemctl stop birdnet_recording.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart birdnet_recording.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now birdnet_recording.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now birdnet_recording.service">Enable</button>
</form>
<form action="" method="POST">
<h3>Chart Viewer</h3>
<button type="submit" name="submit" value="sudo systemctl stop chart_viewer.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart chart_viewer.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now chart_viewer.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now chart_viewer.service">Enable</button>
</form>
<form action="" method="POST">
<h3>Spectrogram Viewer</h3>
<button type="submit" name="submit" value="sudo systemctl stop spectrogram_viewer.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart spectrogram_viewer.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now spectrogram_viewer.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now spectrogram_viewer.service">Enable</button>
</form>
<form action="" method="POST">
<h3>Pushed Notifications</h3>
<button type="submit" name="submit" value="sudo systemctl stop pushed_notifications.service">Stop</button>
<button type="submit" name="submit" value="sudo systemctl restart pushed_notifications.service">Restart</button>
<button type="submit" name="submit" value="sudo systemctl disable --now pushed_notifications.service">Disable</button>
<button type="submit" name="submit" value="sudo systemctl enable --now pushed_notifications.service">Enable</button>
</form>
<form action="" method="POST">
<button type="submit" name="submit" value="restart_services.sh" onclick="return confirm('This will take about 90 seconds.')">Restart All Services</button>
</form>
</div>
+4 -4
View File
@@ -4,8 +4,8 @@ if (file_exists('/home/pi/BirdNET-Pi/thisrun.txt')) {
} elseif (file_exists('/home/pi/BirdNET-Pi/firstrun.ini')) {
$config = parse_ini_file('/home/pi/BirdNET-Pi/firstrun.ini');
}
$refreshtime = $config['RECORDING_LENGTH'];
header("refresh:$refreshtime");
$refresh = $config['RECORDING_LENGTH'];
$time = time();
echo "<img style=\"width:100%;height:100%\" src=\"/spectrogram.png?nocache=$time\">";
header("Refresh: $refresh;");
?>
<body style='background-color:rgb(119, 196, 135)'>
<img src='/spectrogram.png?nocache=<?php echo time();?>' style='display: block; height: 100%; width: 100%;'>
+102 -233
View File
@@ -3,270 +3,139 @@ ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$mysqli = mysqli_connect();
$mysqli->select_db('birds');
if ($mysqli->connect_error) {
die('Connect Error (' .
$mysqli->connect_errno . ') '.
$mysqli->connect_error);
$db = new SQLite3('/home/pi/BirdNET-Pi/scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
if($db == False) {
echo "Database busy";
header("refresh: 0;");
}
$statement = $db->prepare('SELECT Date, Time, File_Name, Com_Name, COUNT(*), MAX(Confidence) FROM detections GROUP BY Com_Name ORDER BY COUNT(*) DESC');
if($statement == False) {
echo "Database busy";
header("refresh: 0;");
}
$result = $statement->execute();
// SQL query to select data from database
$sql = "SELECT COUNT(*) AS 'Total' FROM detections
ORDER BY Date DESC, Time DESC";
$totalcount = $mysqli->query($sql);
$statement2 = $db->prepare('SELECT Date, Time, File_Name, Com_Name, COUNT(*), MAX(Confidence) FROM detections GROUP BY Com_Name ORDER BY Com_Name');
if($statement == False) {
echo "Database busy";
header("refresh: 0;");
}
$result2 = $statement2->execute();
$sql1 = "SELECT Com_Name, COUNT(*), MAX(Confidence)
FROM detections
GROUP BY Com_Name
ORDER BY COUNT(*) DESC";
$stats = $mysqli->query($sql1);
$sql2 = "SELECT COUNT(*) AS 'Total' FROM detections
WHERE Date = CURDATE()";
$todayscount = $mysqli->query($sql2);
$sql3 = "SELECT COUNT(*) AS 'Total' FROM detections
WHERE Date = CURDATE()
AND Time >= DATE_SUB(NOW(),INTERVAL 1 HOUR)";
$lasthourcount = $mysqli->query($sql3);
$sql4 = "SELECT Com_Name, Date, Time, MAX(Confidence)
FROM detections
GROUP BY Com_Name
ORDER BY MAX(Confidence) DESC";
$specieslist = $mysqli->query($sql4);
$speciescount = mysqli_num_rows($specieslist);
$sql5 = "SELECT Com_Name,COUNT(*)
AS 'Total'
FROM detections
GROUP BY Com_Name
ORDER BY Total DESC";
$speciestally = $mysqli->query($sql5);
$getspecies = "SELECT Com_Name from detections
GROUP BY Com_Name";
$result = $mysqli->query($getspecies);
if(isset($_POST['species'])){
$selection = $_POST['species'];
$specificspecies = "SELECT Com_Name, Sci_Name, COUNT(*), MAX(Confidence) from detections
WHERE Com_Name = \"$selection\"";
$specificstats = $mysqli->query($specificspecies);}
$mysqli->close();
$statement3 = $db->prepare("SELECT Com_Name, Sci_Name, COUNT(*), MAX(Confidence), File_Name, Date, Time from detections WHERE Com_Name = \"$selection\"");
if($statement3 == False) {
echo "Database busy";
header("refresh: 0;");
}
$result3 = $statement3->execute();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>BirdNET-Pi DB</title>
<link rel="stylesheet" href="style.css">
<style>
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
</style>
/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
* {
font-family: 'Arial', 'Gill Sans', 'Gill Sans MT',
' Calibri', 'Trebuchet MS', 'sans-serif';
box-sizing: border-box;
}
/* Create two unequal columns that floats next to each other */
.column {
float: left;
padding: 10px;
}
.first {
width: calc(50% - 70px);
}
.second {
width: calc(50% + 70px);
}
.
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
body {
background-color: rgb(119, 196, 135);
}
a {
color:black;
text-decoration: none;
}
.block {
display: block;
width:40%;
margin-left:auto;
border: none;
padding: 5px 5px;
font-size: medium;
cursor: pointer;
text-align: center;
}
img {
width:75%;
}
.center {
display: block;
margin-left: auto;
margin-right: auto;
}
select option {
font-size:large;
}
select {
float:left;
display:block;
margin-right:auto;
font-size:large;
width: 50%;
}
@media screen and (max-width: 800px) {
h3 {
margin-bottom:0px;
text-align:center;
}
form {
text-align:left;
margin-left:0px;
width:100%;
}
.column {
float: none;
width: 100%;
}
input, label, img {
width:100%;
{
}
</style>
</head>
<body style="background-color: rgb(119, 196, 135);">
<section>
<div class="row">
<div class="column first">
<h3>Number of Detections</h3>
<table>
<tr>
<th>Total</th>
<th>Today</th>
<th>Last Hour</th>
<th>Number of Unique Species</th>
</tr>
<tr>
<td><?php while ($row = $totalcount->fetch_assoc()) { echo $row['Total']; };?></td>
<td><?php while ($row = $todayscount->fetch_assoc()) { echo $row['Total']; };?></td>
<td><?php while ($row = $lasthourcount->fetch_assoc()) { echo $row['Total']; };?></td>
<td><?php echo $speciescount;?></td>
</tr>
</table>
<h3>Summary</h3>
<table>
<tr>
<th>Common Name</th>
<th>Occurrences</th>
<th>Max Confidence Score</th>
</tr>
<?php // LOOP TILL END OF DATA
while($rows=$stats ->fetch_assoc())
<body>
<div class="stats">
<div class="column left">
<table>
<?php
while($results=$result2->fetchArray(SQLITE3_ASSOC))
{
$MAX = sprintf("%.1f%%", $rows['MAX(Confidence)'] * 100);
$links = preg_replace('/ /', '_', $rows['Com_Name']);
$links = preg_replace('/\'/', '', $links);
$comname = preg_replace('/ /', '_', $results['Com_Name']);
$comname = preg_replace('/\'/', '', $comname);
$filename = "/By_Date/".$results['Date']."/".$comname."/".$results['File_Name'];
?>
<tr>
<td><a href="../By_Common_Name/<?php echo $links;?>"><?php echo $rows['Com_Name'];?></a></td>
<td><?php echo $rows['COUNT(*)'];?></td>
<td><?php echo $MAX;?></td>
</tr>
<tr>
<form action="" method="POST">
<td><input type="hidden" name="view" value="Species Stats">
<button type="submit" name="species" value="<?php echo $results['Com_Name'];?>"><?php echo $results['Com_Name'];?></button>
</td>
<?php
}
?>
</table>
</div>
<div class="column second">
<form style="margin:0;width:100%;" action="stats.php" method="POST">
<h3>Species Stats</h3>
<select name="species" >
<option value="<?php if(isset($_POST['species'])){echo $selection;}?>"><?php if(isset($_POST['species'])){echo $selection;}else{echo "--Choose Species--";}?></option>
<?php
while($row = $result->fetch_assoc()) {
?>
<option value="<?php echo $row['Com_Name'];?>"><?php echo $row['Com_Name'];?></option>"
</form>
</tr>
</table>
</div>
<div class="column center">
<?php if(!isset($_POST['species'])){
?><p class="centered">Choose a species to load images from Wikimedia Commons.</p>
<?php
}
?>
</select>
<button type="submit" class="block"/>Show Species Statistics</button>
</form>
};?>
<?php if(isset($_POST['species'])){
$species = $_POST['species'];
$str = "<h3>$species</h3>
<table>
<tr>
<th>Common Name</th>
<th>Scientific Name</th>
<th>Occurrences</th>
<th>Highest Confidence Score</th>
<th>Links</th>
</tr>";
echo str_pad($str, 4096);
ob_flush();
flush();
while($rows = $specificstats->fetch_assoc()) {
$count = $rows['COUNT(*)'];
$maxconf = $rows['MAX(Confidence)'];
$name = $rows['Com_Name'];
$sciname = $rows['Sci_Name'];
$dbname = preg_replace('/ /', '_', $rows['Com_Name']);
$dbname = preg_replace('/\'/', '', $dbname);
$dbsciname = preg_replace('/ /', '_', $rows['Sci_Name']);
$imagelink = shell_exec("/home/pi/BirdNET-Pi/scripts/get_image.sh $dbsciname");
$imagecitation = shell_exec("/home/pi/BirdNET-Pi/scripts/get_citation.sh $dbsciname");
$str= "<tr>
<td><a href=\"../By_Common_Name/$dbname\"/>$name</a></td>
<td><a href=\"../By_Scientific_Name/$dbsciname\"/>$sciname</a></td>
<td>$count</td>
<td>$maxconf</td>
<td><a href=\"https://wikipedia.org/wiki/$dbsciname\" target=\"top\"/>Wikipedia</a>, <a href=\"https://allaboutbirds.org/guide/$dbname\" target=\"top\"/>All About Birds</a>
while($results=$result3->fetchArray(SQLITE3_ASSOC)){
$count = $results['COUNT(*)'];
$maxconf = $results['MAX(Confidence)'];
$date = $results['Date'];
$time = $results['Time'];
$name = $results['Com_Name'];
$sciname = $results['Sci_Name'];
$dbsciname = preg_replace('/ /', '_', $sciname);
$comname = preg_replace('/ /', '_', $results['Com_Name']);
$comname = preg_replace('/\'/', '', $comname);
$linkname = preg_replace('/_/', '+', $dbsciname);
$filename = "/By_Date/".$date."/".$comname."/".$results['File_Name'];
echo str_pad("<h3>$species</h3>
<table><tr>
<td><a href=\"https://wikipedia.org/wiki/$dbsciname\" target=\"top\"/><i>$sciname</i></a><br>
<b>Occurrences: </b>$count<br>
<b>Max Confidence: </b>$maxconf<br>
<b>Best Recording: </b>$date $time<br>
<a href=\"https://allaboutbirds.org/guide/$comname\" target=\"top\"/>All About Birds</a><br>
<video controls poster=\"$filename.png\" title=\"$filename\"><source src=\"$filename\"></video></td>
</tr>
</table>";
echo str_pad($str, 4096);
</table>
<p>Loading Images from <a href=\"https://commons.wikimedia.org/w/index.php?search=$linkname&title=Special:MediaSearch&go=Go&type=image\" target=\"_blank\">Wikimedia Commons</a></p>", '6096');
ob_flush();
flush();
echo "<img class=\"center\" src=\"$imagelink\">
<pre>$imagecitation</pre></td>
</div>
</div>
</div>";
$imagelink = "https://commons.wikimedia.org/w/index.php?search=$linkname&title=Special:MediaSearch&go=Go&type=image";
$homepage = file_get_contents($imagelink);
preg_match_all("{<img\\s*(.*?)src=('.*?'|\".*?\"|[^\\s]+)(.*?)\\s*/?>}ims", $homepage, $matches, PREG_SET_ORDER);
foreach ($matches as $val) {
$pos = strpos($val[2],"/");
$link = substr($val[2],1,-1);
if($pos !== 1 && strpos($link, "upload") == true && strpos($link, "CentralAutoLogin") == false)
echo "<img src=\"$link\">";
}
}}
?>
</section>
<br><br><br>
<table>
<?php
while($results=$result->fetchArray(SQLITE3_ASSOC))
{
$comname = preg_replace('/ /', '_', $results['Com_Name']);
$comname = preg_replace('/\'/', '', $comname);
$filename = "/By_Date/".$results['Date']."/".$comname."/".$results['File_Name'];
?>
<tr>
<form action="" method="POST">
<td><input type="hidden" name="view" value="Species Stats">
<button type="submit" name="species" value="<?php echo $results['Com_Name'];?>"><?php echo $results['Com_Name'];?></button><br><b>Occurrences:</b> <?php echo $results['COUNT(*)'];?><br>
<b>Max Confidence:</b> <?php echo $results['MAX(Confidence)'];?><br>
<b>Best Recording:</b> <?php echo $results['Date']." ".$results['Time'];?><br><video controls poster="<?php echo $filename.".png";?>" preload="none" title="<?php echo $filename;?>"><source src="<?php echo $filename;?>" type="audio/mp3"></video></td>
</tr>
<?php
}
?>
</table>
</form>
</div>
</div>
</body>
</html>
+1 -1
View File
@@ -6,7 +6,7 @@ services=(birdnet_recording.service
birdnet_analysis.service
birdnet_server.service
chart_viewer.service
extraction.timer
extraction.service
spectrogram_viewer.service)
for i in "${services[@]}";do
+17 -102
View File
@@ -1,103 +1,18 @@
<html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
font-family: 'Arial', 'Gill Sans', 'Gill Sans MT',
' Calibri', 'Trebuchet MS', 'sans-serif';
box-sizing: border-box;
box-sizing: border-box;
}
/* Create two unequal columns that floats next to each other */
.column {
float: left;
padding: 10px;
}
.first {
width: calc(50% - 70px);
}
.second {
width: calc(50% - 30px);
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
body {
background-color: rgb(119, 196, 135);
}
a {
text-decoration: none;
color: white;
}
.block {
display: block;
font-weight: bold;
width:100%;
border: none;
background-color: #04AA6D;
padding: 20px 20px;
color: white;
font-size: medium;
cursor: pointer;
text-align: center;
}
@media screen and (max-width: 800px) {
.column {
float: none;
width: 100%;
}
}
</style>
<body style="background-color: rgb(119, 196, 135);">
<div class="row">
<div class="column first">
<form action="" method="POST" onclick="return confirm('Are you sure you want to reboot?')">
<input type="hidden" name="submit" value="sudo reboot">
<button type="submit" class="block">Reboot</button>
</form>
<form action="" method="POST" onclick="return confirm('BE SURE TO STASH ANY LOCAL CHANGES YOU HAVE MADE TO THE SYSTEM BEFORE UPDATING!!!')">
<input type="hidden" name="submit" value="update_birdnet.sh">
<button style="color:blue;" type="submit" class="block">Update</button>
</form>
<form action="" method="POST" onclick="return confirm('Are you sure you want to shutdown?')">
<input type="hidden" name="submit" value="sudo shutdown now">
<button style="color: red;" type="submit" class="block">Shutdown</button>
</form>
<form action="" method="POST" onclick="return confirm('Clear ALL Data? This cannot be undone.')">
<input type="hidden" name="submit" value="clear_all_data.sh">
<button style="color: red;" type="submit" class="block">Clear ALL data</button>
</form>
</div>
<div class="column second">
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if(isset($_POST['submit'])){
$command = $_POST['submit'];
if($command == 'update_birdnet.sh'){
$str= "<h3>Updating . . . </h3>
<p>Please wait 60 seconds</p>";
echo str_pad($str, 4096);
ob_flush();
flush();
}
if(isset($command)){
$results = shell_exec("$command 2>&1");
echo "</div>
</div>
<pre>$results</pre>";
}
}
ob_end_flush();
?>
</body>
<br>
<br>
<div class="systemcontrols">
<form action="" method="POST">
<button type="submit" name="submit" value="sudo reboot" onclick="return confirm('Are you sure you want to reboot?')">Reboot</button>
</form>
<form action="" method="POST">
<button type="submit" name="submit" value="update_birdnet.sh" onclick="return confirm('BE SURE TO STASH ANY LOCAL CHANGES YOU HAVE MADE TO THE SYSTEM BEFORE UPDATING!!!')" >Update</button>
</form>
<form action="" method="POST">
<button type="submit" name="submit" value="sudo shutdown now" onclick="return confirm('Are you sure you want to shutdown?')">Shutdown</button>
</form>
<form action="" method="POST">
<button type="submit" name="submit" value="sudo -upi clear_all_data.sh" onclick="return confirm('Clear ALL Data? This cannot be undone.')">Clear ALL data</button>
</form>
</div>
+110
View File
@@ -0,0 +1,110 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$db = new SQLite3('/home/pi/BirdNET-Pi/scripts/birds.db', SQLITE3_OPEN_CREATE | SQLITE3_OPEN_READWRITE);
if($db == False){
echo "Database is busy";
header("refresh: 0;");
}
$statement0 = $db->prepare('SELECT Time, Com_Name, Sci_Name, Confidence, File_Name FROM detections WHERE Date == Date(\'now\', \'localtime\') ORDER BY Time DESC');
if($statement0 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result0 = $statement0->execute();
$statement1 = $db->prepare('SELECT COUNT(*) FROM detections');
if($statement1 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result1 = $statement1->execute();
$totalcount = $result1->fetchArray(SQLITE3_ASSOC);
$statement2 = $db->prepare('SELECT COUNT(*) FROM detections WHERE Date == DATE(\'now\', \'localtime\')');
if($statement2 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result2 = $statement2->execute();
$todaycount = $result2->fetchArray(SQLITE3_ASSOC);
$statement3 = $db->prepare('SELECT COUNT(*) FROM detections WHERE Date == Date(\'now\', \'localtime\') AND TIME >= TIME(\'now\', \'localtime\', \'-1 hour\')');
if($statement3 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result3 = $statement3->execute();
$hourcount = $result3->fetchArray(SQLITE3_ASSOC);
$statement4 = $db->prepare('SELECT Com_Name, Sci_Name, Time, Confidence FROM detections LIMIT 1');
if($statement4 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result4 = $statement4->execute();
$mostrecent = $result4->fetchArray(SQLITE3_ASSOC);
$statement5 = $db->prepare('SELECT COUNT(DISTINCT(Com_Name)) FROM detections');
if($statement4 == False){
echo "Database is busy";
header("refresh: 0;");
}
$result5 = $statement5->execute();
$speciestally = $result5->fetchArray(SQLITE3_ASSOC);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>BirdNET-Pi DB</title>
<style>
</style>
</head>
<div class="viewdb">
<h3>Number of Detections</h3>
<table>
<tr>
<th>Total</th>
<th>Today</th>
<th>Last Hour</th>
<th>Number of Unique Species</th>
</tr>
<tr>
<td><?php echo $totalcount['COUNT(*)'];?></td>
<form action="" method="POST">
<td><input type="hidden" name="view" value="Recordings"><button type="submit" name="date" value="<?php echo date('Y-m-d');?>"><?php echo $todaycount['COUNT(*)'];?></button></td>
</form>
<td><?php echo $hourcount['COUNT(*)'];?></td>
<form action="" method="POST">
<td><button type="submit" name="view" value="Species Stats"><?php echo $speciestally['COUNT(DISTINCT(Com_Name))'];?></button></td>
</form>
</tr>
</table>
<h3>Today's Detections</h3>
<table>
<?php
while($todaytable=$result0->fetchArray(SQLITE3_ASSOC))
{
$comname = preg_replace('/ /', '_', $todaytable['Com_Name']);
$comname = preg_replace('/\'/', '_', $comname);
$filename = "/By_Date/".date('Y-m-d')."/".$comname."/".$todaytable['File_Name'];
$sciname = preg_replace('/ /', '_', $todaytable['Sci_Name']);
?>
<tr>
<td><?php echo $todaytable['Time'];?><br>
<b><a class="a2" href="https://allaboutbirds.org/guide/<?php echo $comname;?>" target="top"><?php echo $todaytable['Com_Name'];?></a></b><br>
<a class="a2" href="https://wikipedia.org/wiki/<?php echo $sciname;?>" target="top"><i><?php echo $todaytable['Sci_Name'];?></i></a><br>
<b>Confidence:</b> <?php echo $todaytable['Confidence'];?><br>
<a href="<?php echo $filename;?>"><img src="<?php echo $filename.".png";?>"></a></td>
<?php }?>
</tr>
</table>
</div>
+4 -51
View File
@@ -1,59 +1,12 @@
#!/usr/bin/env bash
# Update BirdNET-Pi
source /etc/birdnet/birdnet.conf
trap 'rm -f ${tmpfile}' EXIT
trap 'exit 1' SIGINT SIGHUP
USER=pi
HOME=/home/pi
my_dir=${HOME}/BirdNET-Pi/scripts
tmpfile=$(mktemp)
services=$(awk '/service/ && /systemctl/ && !/php/ {print $3}' ${my_dir}/install_services.sh | sort)
remove_services() {
for i in "${services[@]}"; do
if [ -L /etc/systemd/system/multi-user.target.wants/"${i}" ];then
sudo systemctl kill "${i}"
sudo systemctl disable "${i}"
fi
if [ -f /etc/systemd/system/"${i}" ];then
sudo rm /etc/systemd/system/"${i}"
fi
if [ -d /etc/systemd/system/"${i}" ];then
sudo rm -drf /etc/systemd/system/"${i}"
fi
done
remove_icecast
remove_crons
}
remove_crons() {
sudo sed -i '/birdnet/,+1d' /etc/crontab
}
remove_icecast() {
if [ -f /etc/init.d/icecast2 ];then
sudo /etc/init.d/icecast2 stop
sudo systemctl disable --now icecast2
fi
}
remove_scripts() {
for i in "${scripts[@]}";do
if [ -L "/usr/local/bin/${i}" ];then
sudo rm -v "/usr/local/bin/${i}"
fi
done
}
# Stage 1 removes old stuff
remove_services
remove_scripts
# Backup labels.txt
sudo -u${USER} cp -f ~/BirdNET-Pi/model/labels.txt{,.bak}
# Stage 2 does a git pull to fetch new things
sudo -u${USER} git -C ${HOME}/BirdNET-Pi checkout -f
sudo -u${USER} git -C ${HOME}/BirdNET-Pi pull -f
# Trigger the new update_birdnet2.sh
sudo -u${USER} ${my_dir}/update_birdnet2.sh
sudo -u${USER} git -C /home/pi/BirdNET-Pi stash
sudo -u${USER} git -C /home/pi/BirdNET-Pi pull -f
sudo systemctl daemon-reload
sudo -u${USER} git -C /home/pi/BirdNET-Pi stash pop
-59
View File
@@ -1,59 +0,0 @@
#!/usr/bin/env bash
# Second stage of update
USER=pi
birdnet_conf=/home/pi/BirdNET-Pi/birdnet.conf
my_dir=${HOME}/BirdNET-Pi/scripts
# Stage 1 updates the services
sudo ${my_dir}/update_services.sh
# Stage 1.5: adding new birdnet.conf entries
if ! grep FULL_DISK ${birdnet_conf} &> /dev/null;then
cat << EOF >> ${birdnet_conf}
## FULL_DISK can be set to configure how the system reacts to a full disk
## purge = Remove the oldest day's worth of recordings
## keep = Keep all data and `stop_core_services.sh`
FULL_DISK=purge
EOF
fi
if ! grep AUDIOFMT ${birdnet_conf} &> /dev/null;then
cat<< EOF >> ${birdnet_conf}
## AUDIOFMT set the audio format that sox should use for the extractions.
## The default is mp3. Available formats are: 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
## Note: Most have not been tested.
AUDIOFMT=mp3
EOF
fi
sudo -u${USER} sed -i 's/EXTRACTIONLOG_URL/WEBTERMINAL_URL/g' ${birdnetconf}
# Replace Backup labels.txt
sudo -u${USER} cp -f ~/BirdNET-Pi/model/labels.txt.bak ~/BirdNET-Pi/model/labels.txt
# Stage 2 restarts the services
sudo systemctl daemon-reload
sudo systemctl stop birdnet_recording.service
sudo rm -rf ${RECS_DIR}/$(date +%B-%Y/%d-%A)/*
services=(web_terminal.service
spectrogram_viewer.service
pushed_notifications.service
livestream.service
icecast2.service
extraction.timer
extraction.service
chart_viewer.service
birdnet_recording.service
birdnet_log.service)
for i in "${services[@]}";do
sudo systemctl restart "${i}"
done
+22 -70
View File
@@ -14,6 +14,12 @@ cat << EOF > /etc/caddy/Caddyfile
http://localhost http://$(hostname).local ${BIRDNETPI_URL} {
root * ${EXTRACTED}
file_server browse
handle /By_Date/* {
file_server browse
}
handle /Charts/* {
file_server browse
}
basicauth /Processed* {
birdnet ${HASHWORD}
}
@@ -26,8 +32,14 @@ http://localhost http://$(hostname).local ${BIRDNETPI_URL} {
basicauth /phpsysinfo* {
birdnet ${HASHWORD}
}
basicauth /terminal* {
birdnet ${HASHWORD}
}
reverse_proxy /stream localhost:8000
php_fastcgi unix//run/php/php7.4-fpm.sock
reverse_proxy /log* localhost:8080
reverse_proxy /stats* localhost:8501
reverse_proxy /terminal* localhost:8888
}
EOF
else
@@ -35,79 +47,19 @@ else
http://localhost http://$(hostname).local ${BIRDNETPI_URL} {
root * ${EXTRACTED}
file_server browse
handle /By_Date/* {
file_server browse
}
handle /Charts/* {
file_server browse
}
reverse_proxy /stream localhost:8000
php_fastcgi unix//run/php/php7.4-fpm.sock
reverse_proxy /log* localhost:8080
reverse_proxy /stats* localhost:8501
reverse_proxy /terminal* localhost:8888
}
EOF
fi
if [ ! -z ${WEBTERMINAL_URL} ] && [ ! -z ${HASHWORD} ];then
cat << EOF >> /etc/caddy/Caddyfile
${WEBTERMINAL_URL} {
basicauth {
birdnet ${HASHWORD}
}
reverse_proxy localhost:8888
}
EOF
elif [ ! -z ${WEBTERMINAL_URL} ] && [ -z ${HASHWORD} ];then
cat << EOF >> /etc/caddy/Caddyfile
${WEBTERMINAL_URL} {
reverse_proxy localhost:8888
}
EOF
fi
if [ ! -z ${BIRDNETLOG_URL} ];then
cat << EOF >> /etc/caddy/Caddyfile
${BIRDNETLOG_URL} {
reverse_proxy localhost:8080
}
EOF
fi
################################################################################
sudo -u${USER} git -C /home/pi/BirdNET-Pi checkout -f homepage/*
sudo -u${USER} git -C /home/pi/BirdNET-Pi checkout -f scripts/*
sudo -u${USER} git -C /home/pi/BirdNET-Pi checkout -f scripts/*/*
sudo -u${USER} sed -i "s/https:\/\/v2.wttr.in\//https:\/\/v2.wttr.in\/"${LATITUDE},${LONGITUDE}"/g" $(dirname ${my_dir})/homepage/menu.html
if [ ! -z ${BIRDNETLOG_URL} ];then
BIRDNETLOG_URL="$(echo ${BIRDNETLOG_URL} | sed 's/\/\//\\\/\\\//g')"
else
BIRDNETLOG_URL="$(echo http://$(hostname).local:8080 | sed 's/\/\//\\\/\\\//g')"
fi
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*/*.php
if [ ! -z ${WEBTERMINAL_URL} ];then
WEBTERMINAL_URL="$(echo ${WEBTERMINAL_URL} | sed 's/\/\//\\\/\\\//g')"
else
WEBTERMINAL_URL="$(echo http://$(hostname).local:8888 | sed 's/\/\//\\\/\\\//g')"
fi
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*/*.php
if [ -z ${BIRDNETPI_URL} ];then
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*/*.php
else
BIRDNETPI_URL="$(echo ${BIRDNETPI_URL} | sed 's/\/\//\\\/\\\//g')"
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*/*.php
fi
systemctl reload caddy
sudo systemctl reload caddy
-11
View File
@@ -1,11 +0,0 @@
#!/usr/bin/env bash
# Update database password
source /etc/birdnet/birdnet.conf
mysql -e "
SET PASSWORD FOR 'birder'@'localhost' = PASSWORD('${DB_PWD}');
FLUSH PRIVILEGES";
sed -i "s/mysqli.default_host =.*/mysqli.default_host = localhost/g" /etc/php/7.4/fpm/php.ini
sed -i "s/mysqli.default_user =.*/mysqli.default_user = birder/g" /etc/php/7.4/fpm/php.ini
sed -i "s/mysqli.default_pw =.*/mysqli.default_pw = ${DB_PWD}/g" /etc/php/7.4/fpm/php.ini
systemctl restart php7.4-fpm.service
-38
View File
@@ -1,38 +0,0 @@
<?php
$timer=60;
header( "refresh:$timer;url=/overview.php" );
?>
<html lang="en">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head></head>
<body style="background-color: rgb(119, 196, 135)">
<script>
function countDown(secs,elem) {
var element = document.getElementById(elem);
element.innerHTML = "Update in progress. Please allow another "+secs+" seconds for it to complete.";
if(secs < 1) {
clearTimeout(timer);
element.innerHTML = '<h4>Let\'s see</h4>';
}
secs--;
var timer = setTimeout('countDown('+secs+',"'+elem+'")',1000);
}
</script>
<div id="status"style="font-size:30px;"></div>
<script>countDown(<?php echo $timer;?>,"status");</script>
</body>
</html>
-591
View File
@@ -1,591 +0,0 @@
#!/usr/bin/env bash
# This reinstalls the services
set -x # Uncomment to enable debugging
trap 'rm -f ${tmpfile}' EXIT
trap 'exit 1' SIGINT SIGHUP
USER=pi
HOME=/home/pi
my_dir=${HOME}/BirdNET-Pi/scripts
tmpfile=$(mktemp)
nomachine_url="https://download.nomachine.com/download/7.7/Arm/nomachine_7.7.4_1_arm64.deb"
gotty_url="https://github.com/yudai/gotty/releases/download/v1.0.1/gotty_linux_arm.tar.gz"
config_file="$(dirname ${my_dir})/birdnet.conf"
install_ftpd() {
if ! [ -f /etc/ftpuseres ];then
apt -y install ftpd
fi
}
install_lynx() {
if ! which lynx &> /dev/null;then
apt -y install lynx
fi
}
install_scripts() {
echo "Installing BirdNET-Pi scripts to /usr/local/bin"
ln -sf ${my_dir}/* /usr/local/bin/
rm /usr/local/bin/index.html
}
install_mariadb() {
if ! which mysql &> /dev/null;then
echo "Installing MariaDB Server"
apt -qqy install mariadb-server
echo "MariaDB Installed"
fi
source /etc/os-release
if [[ "${VERSION_CODENAME}" == "buster" ]];then
USER=${USER} ${my_dir}/update_db_pwd_buster.sh
elif [[ "${VERSION_CODENAME}" == "bullseye" ]];then
mysql -e "
SET PASSWORD FOR 'birder'@'localhost' = PASSWORD('${DB_PWD}');
FLUSH PRIVILEGES";
sed -i "s/mysqli.default_host =.*/mysqli.default_host = localhost/g" /etc/php/7.4/fpm/php.ini
sed -i "s/mysqli.default_user =.*/mysqli.default_user = birder/g" /etc/php/7.4/fpm/php.ini
sed -i "s/mysqli.default_pw =.*/mysqli.default_pw = ${DB_PWD}/g" /etc/php/7.4/fpm/php.ini
fi
}
install_birdnet_analysis() {
echo "Installing the birdnet_analysis.service"
cat << EOF > /etc/systemd/system/birdnet_analysis.service
[Unit]
Description=BirdNET Analysis
After=birdnet_server.service
Requires=birdnet_server.service
[Service]
Restart=always
Type=simple
RestartSec=2
User=${USER}
ExecStart=/usr/local/bin/birdnet_analysis.sh
[Install]
WantedBy=multi-user.target
EOF
systemctl enable birdnet_analysis.service
}
install_birdnet_server() {
echo "Installing the birdnet_server.service"
cat << EOF > /etc/systemd/system/birdnet_server.service
[Unit]
Description=BirdNET Analysis Server
Before=birdnet_analysis.service
[Service]
Restart=always
Type=simple
RestartSec=2
User=${USER}
ExecStart=/usr/local/bin/server.py
[Install]
WantedBy=multi-user.target
EOF
systemctl enable birdnet_server.service
}
install_extraction_service() {
echo "Installing the extraction.service and extraction.timer"
cat << EOF > /etc/systemd/system/extraction.service
[Unit]
Description=BirdNET BirdSound Extraction
[Service]
Restart=on-failure
RestartSec=3
Type=simple
User=${USER}
ExecStart=/usr/local/bin/extract_new_birdsounds.sh
[Install]
WantedBy=multi-user.target
EOF
cat << EOF > /etc/systemd/system/extraction.timer
[Unit]
Description=BirdNET BirdSound Extraction Timer
Requires=extraction.service
[Timer]
Unit=extraction.service
OnCalendar=*:*:0/10
[Install]
WantedBy=multi-user.target
EOF
systemctl enable extraction.timer
systemctl enable extraction.service
}
install_pushed_notifications() {
echo "Installing Pushed.co mobile notifications"
cat << EOF > /etc/systemd/system/pushed_notifications.service
[Unit]
Description=BirdNET-Pi Pushed.co Notifications
[Service]
Restart=on-success
RestartSec=3
Type=simple
User=pi
ExecStart=/usr/local/bin/species_notifier.sh
[Install]
WantedBy=multi-user.target
EOF
systemctl enable pushed_notifications.service
}
create_necessary_dirs() {
echo "Creating necessary directories"
[ -d ${EXTRACTED} ] || sudo -u ${USER} mkdir -p ${EXTRACTED}
[ -d ${EXTRACTED}/By_Date ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/By_Date
[ -d ${EXTRACTED}/By_Common_Name ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/By_Common_Name
[ -d ${EXTRACTED}/By_Scientific_Name ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/By_Scientific_Name
[ -d ${EXTRACTED}/Charts ] || sudo -u ${USER} mkdir -p ${EXTRACTED}/Charts
[ -d ${PROCESSED} ] || sudo -u ${USER} mkdir -p ${PROCESSED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/homepage/* ${EXTRACTED}
if [ ! -z ${BIRDNETLOG_URL} ];then
BIRDNETLOG_URL="$(echo ${BIRDNETLOG_URL} | sed 's/\/\//\\\/\\\//g')"
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8080/${BIRDNETLOG_URL}/g" $(dirname ${my_dir})/scripts/*/*.php
fi
if [ ! -z ${WEBTERMINAL_URL} ];then
WEBTERMINAL_URL="$(echo ${WEBTERMINAL_URL} | sed 's/\/\//\\\/\\\//g')"
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local:8888/${WEBTERMINAL_URL}/g" $(dirname ${my_dir})/scripts/*/*.php
fi
sudo -u ${USER} ln -fs $(dirname ${my_dir})/model/labels.txt ${my_dir}/
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts ${EXTRACTED}
if [ -z ${BIRDNETPI_URL} ];then
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/birdnetpi.local/$(hostname).local/g" $(dirname ${my_dir})/scripts/*/*.php
else
BIRDNETPI_URL="$(echo ${BIRDNETPI_URL} | sed 's/\/\//\\\/\\\//g')"
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/homepage/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.html
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*.php
sudo -u${USER} sed -i "s/http:\/\/birdnetpi.local/${BIRDNETPI_URL}/g" $(dirname ${my_dir})/scripts/*/*.php
fi
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/spectrogram.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/viewday.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/overview.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/stats.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/viewdb.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/scripts/history.php ${EXTRACTED}
sudo -u ${USER} ln -fs $(dirname ${my_dir})/homepage/images/favicon.ico ${EXTRACTED}
sudo -u ${USER} ln -fs ${HOME}/phpsysinfo ${EXTRACTED}
sudo -u ${USER} cp -f $(dirname ${my_dir})/templates/phpsysinfo.ini ${HOME}/phpsysinfo/
sudo -u ${USER} cp -f $(dirname ${my_dir})/templates/green_bootstrap.css ${HOME}/phpsysinfo/templates/
sudo -u ${USER} cp -f $(dirname ${my_dir})/templates/index_bootstrap.html ${HOME}/phpsysinfo/templates/html
echo "Setting Wttr.in URL to "${LATITUDE}", "${LONGITUDE}""
sudo -u${USER} sed -i "s/https:\/\/v2.wttr.in\//https:\/\/v2.wttr.in\/"${LATITUDE},${LONGITUDE}"/g" $(dirname ${my_dir})/homepage/menu.html
chmod -R g+rw $(dirname ${my_dir})
chmod -R g+rw ${RECS_DIR}
}
generate_BirdDB() {
echo "Generating BirdDB.txt"
if ! [ -f $(dirname ${my_dir})/BirdDB.txt ];then
sudo -u ${USER} touch $(dirname ${my_dir})/BirdDB.txt
echo "Date;Time;Sci_Name;Com_Name;Confidence;Lat;Lon;Cutoff;Week;Sens;Overlap" | sudo -u ${USER} tee -a $(dirname ${my_dir})/BirdDB.txt
elif ! grep Date $(dirname ${my_dir})/BirdDB.txt;then
sudo -u ${USER} sed -i '1 i\Date;Time;Sci_Name;Com_Name;Confidence;Lat;Lon;Cutoff;Week;Sens;Overlap' $(dirname ${my_dir})/BirdDB.txt
fi
ln -sf $(dirname ${my_dir})/BirdDB.txt ${my_dir}/BirdDB.txt &&
chown pi:pi ${my_dir}/BirdDB.txt && chmod g+rw ${my_dir}/BirdDB.txt
}
install_alsa() {
echo "Checking for alsa-utils and pulseaudio"
if which arecord &> /dev/null ;then
echo "alsa-utils installed"
else
echo "Installing alsa-utils"
apt install -qqy alsa-utils
echo "alsa-utils installed"
fi
if which pulseaudio &> /dev/null;then
echo "PulseAudio installed"
else
echo "Installing pulseaudio"
apt install -qqy pulseaudio
echo "PulseAudio installed"
fi
if ! [ -d /etc/lightdm ];then
systemctl set-default multi-user.target
ln -fs /lib/systemd/system/getty@.service /etc/systemd/system/getty.target.wants/getty@tty1.service
cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin $USER --noclear %I \$TERM
EOF
fi
}
install_recording_service() {
echo "Installing birdnet_recording.service"
cat << EOF > /etc/systemd/system/birdnet_recording.service
[Unit]
Description=BirdNET Recording
[Service]
Environment=XDG_RUNTIME_DIR=/run/user/1000
Restart=always
Type=simple
RestartSec=3
User=${USER}
ExecStart=/usr/local/bin/birdnet_recording.sh
[Install]
WantedBy=multi-user.target
EOF
systemctl enable birdnet_recording.service
}
install_caddy() {
if ! which caddy &> /dev/null ;then
echo "Installing Caddy"
curl -1sLf \
'https://dl.cloudsmith.io/public/caddy/stable/setup.deb.sh' \
| sudo -E bash
apt install -qqy caddy=2.4.5
systemctl enable --now caddy
else
echo "Caddy is installed"
systemctl enable --now caddy
fi
}
install_Caddyfile() {
echo "Installing the Caddyfile"
[ -d /etc/caddy ] || mkdir /etc/caddy
if [ -f /etc/caddy/Caddyfile ];then
cp /etc/caddy/Caddyfile{,.original}
fi
php_version="$(awk -F'php' '{print $3}' <(ls -l $(which /etc/alternatives/php)))"
if ! [ -z ${CADDY_PWD} ];then
HASHWORD=$(caddy hash-password -plaintext ${CADDY_PWD})
cat << EOF > /etc/caddy/Caddyfile
http://localhost http://$(hostname).local ${BIRDNETPI_URL} {
root * ${EXTRACTED}
file_server browse
basicauth /Processed* {
birdnet ${HASHWORD}
}
basicauth /scripts* {
birdnet ${HASHWORD}
}
basicauth /stream {
birdnet ${HASHWORD}
}
basicauth /phpsysinfo* {
birdnet ${HASHWORD}
}
reverse_proxy /stream localhost:8000
php_fastcgi unix//run/php/php${php_version}-fpm.sock
}
EOF
else
cat << EOF > /etc/caddy/Caddyfile
http://localhost http://$(hostname).local ${BIRDNETPI_URL} {
root * ${EXTRACTED}
file_server browse
reverse_proxy /stream localhost:8000
php_fastcgi unix//run/php/php${php_version}-fpm.sock
}
EOF
fi
if [ ! -z ${WEBTERMINAL_URL} ] && [ ! -z ${HASHWORD} ];then
cat << EOF >> /etc/caddy/Caddyfile
${WEBTERMINAL_URL} {
basicauth {
birdnet ${HASHWORD}
}
reverse_proxy localhost:8888
}
EOF
elif [ ! -z ${WEBTERMINAL_URL} ] && [ -z ${HASHWORD} ];then
cat << EOF >> /etc/caddy/Caddyfile
${WEBTERMINAL_URL} {
reverse_proxy localhost:8888
}
EOF
fi
if [ ! -z ${BIRDNETLOG_URL} ];then
cat << EOF >> /etc/caddy/Caddyfile
${BIRDNETLOG_URL} {
reverse_proxy localhost:8080
}
EOF
fi
}
update_etc_hosts() {
sed -ie s/'$(hostname).local'/"$(hostname).local ${BIRDNETPI_URL//https:\/\/} ${WEBTERMINAL_URL//https:\/\/} ${BIRDNETLOG_URL//https:\/\/}"/g /etc/hosts
}
install_avahi_aliases() {
echo "Installing Avahi Services"
if ! which avahi-publish &> /dev/null; then
echo "Installing avahi-utils"
apt install -y avahi-utils &> /dev/null
fi
echo "Installing avahi-alias service"
cat << 'EOF' > /etc/systemd/system/avahi-alias@.service
[Unit]
Description=Publish %I as alias for %H.local via mdns
After=network.target network-online.target
Requires=network-online.target
[Service]
Restart=always
RestartSec=3
Type=simple
ExecStart=/bin/bash -c "/usr/bin/avahi-publish -a -R %I $(hostname -I |cut -d' ' -f1)"
[Install]
WantedBy=multi-user.target
EOF
systemctl enable avahi-alias@"$(hostname)".local.service
}
install_spectrogram_service() {
cat << EOF > /etc/systemd/system/spectrogram_viewer.service
[Unit]
Description=BirdNET-Pi Spectrogram Viewer
[Service]
Restart=always
RestartSec=10
Type=simple
User=${USER}
ExecStart=/usr/local/bin/spectrogram.sh
[Install]
WantedBy=multi-user.target
EOF
systemctl enable spectrogram_viewer.service
}
install_chart_viewer_service() {
echo "Installing the chart_viewer.service"
cat << EOF > /etc/systemd/system/chart_viewer.service
[Unit]
Description=BirdNET-Pi Chart Viewer Service
[Service]
Restart=always
RestartSec=300
Type=simple
User=pi
ExecStart=/usr/local/bin/daily_plot.py
[Install]
WantedBy=multi-user.target
EOF
systemctl enable chart_viewer.service
}
install_gotty_logs() {
echo "Installing GoTTY logging"
if ! which gotty &> /dev/null;then
echo "Installing GoTTY binary"
wget -c ${gotty_url} -O - | tar -xz -C /usr/local/bin/
fi
sudo -u ${USER} ln -sf $(dirname ${my_dir})/templates/gotty \
${HOME}/.gotty
sudo -u ${USER} ln -sf $(dirname ${my_dir})/templates/bashrc \
${HOME}/.bashrc
echo "Installing the birdnet_log.service"
cat << EOF > /etc/systemd/system/birdnet_log.service
[Unit]
Description=BirdNET Analysis Log
[Service]
Restart=on-failure
RestartSec=3
Type=simple
User=${USER}
Environment=TERM=xterm-256color
ExecStart=/usr/local/bin/gotty -p 8080 --title-format "BirdNET-Pi Log" birdnet_log.sh
[Install]
WantedBy=multi-user.target
EOF
systemctl enable birdnet_log.service
echo "Installing the Web Terminal"
cat << EOF > /etc/systemd/system/web_terminal.service
[Unit]
Description=BirdNET-Pi Web Terminal
[Service]
Restart=on-failure
RestartSec=3
Type=simple
User=${USER}
Environment=TERM=xterm-256color
ExecStart=/usr/local/bin/gotty -w -p 8888 --title-format "BirdNET-Pi Terminal" bash
[Install]
WantedBy=multi-user.target
EOF
systemctl enable web_terminal.service
}
install_sox() {
if which sox | grep mp3 &> /dev/null;then
echo "Sox is installed"
else
echo "Installing sox"
apt install -y sox libsox-fmt-mp3
echo "Sox installed"
fi
}
install_php() {
if ! which php &> /dev/null || ! which php-fpm || ! apt list --installed | grep php-xml;then
echo "Installing PHP modules"
apt install -qqy php php-fpm php-mysql php-xml php-zip
else
echo "PHP and PHP-FPM installed"
fi
echo "Configuring PHP for Caddy"
sed -i 's/www-data/caddy/g' /etc/php/*/fpm/pool.d/www.conf
echo "Adding Caddy sudoers rule"
cat << EOF > /etc/sudoers.d/010_caddy-nopasswd
caddy ALL=(ALL) NOPASSWD: ALL
EOF
chmod 0440 /etc/sudoers.d/010_caddy-nopasswd
if [ ! -d ${HOME}/phpsysinfo ];then
echo "Fetching phpSysInfo"
sudo -u ${USER} git clone https://github.com/phpsysinfo/phpsysinfo.git \
${HOME}/phpsysinfo
fi
}
install_icecast() {
if ! which icecast2;then
echo "Installing IceCast2"
echo "icecast2 icecast2/icecast-setup boolean false" | debconf-set-selections
apt install -qqy icecast2
config_icecast
systemctl enable icecast2.service
/etc/init.d/icecast2 start
else
echo "Icecast2 is installed"
config_icecast
systemctl enable icecast2.service
/etc/init.d/icecast2 start
fi
}
config_icecast() {
if [ -f /etc/icecast2/icecast.xml ];then
cp /etc/icecast2/icecast.xml{,.prebirdnetpi}
fi
sed -i 's/>admin</>birdnet</g' /etc/icecast2/icecast.xml
passwords=("source-" "relay-" "admin-" "master-" "")
for i in "${passwords[@]}";do
sed -i "s/<${i}password>.*<\/${i}password>/<${i}password>${ICE_PWD}<\/${i}password>/g" /etc/icecast2/icecast.xml
done
}
install_livestream_service() {
echo "Installing Live Stream service"
cat << EOF > /etc/systemd/system/livestream.service
[Unit]
Description=BirdNET-Pi Live Stream
After=network-online.target
Requires=network-online.target
[Service]
Environment=XDG_RUNTIME_DIR=/run/user/1000
Restart=always
Type=simple
RestartSec=3
User=${USER}
ExecStart=/usr/local/bin/livestream.sh
[Install]
WantedBy=multi-user.target
EOF
systemctl enable livestream.service
}
install_nomachine() {
if [ ! -d /usr/share/NX ] && [ -d /etc/lightdm ];then
echo "Installing NoMachine"
curl -s -o ${HOME}/nomachine.deb -O "${nomachine_url}"
apt install -y ${HOME}/nomachine.deb
rm -f ${HOME}/nomachine.deb
echo "Enabling VNC"
systemctl enable --now vncserver-x11-serviced.service
fi
}
install_cleanup_cron() {
echo "Installing the cleanup.cron"
cat $(dirname ${my_dir})/templates/cleanup.cron >> /etc/crontab
}
install_selected_services() {
sudo apt -qqqq update
install_scripts
install_birdnet_analysis
install_birdnet_server
if [[ "${DO_EXTRACTIONS}" =~ [Yy] ]];then
install_extraction_service
fi
if [[ "${DO_RECORDING}" =~ [Yy] ]];then
install_alsa
install_recording_service
fi
install_php
install_caddy
install_Caddyfile
update_etc_hosts
install_avahi_aliases
install_gotty_logs
install_sox
install_mariadb
install_spectrogram_service
install_chart_viewer_service
install_pushed_notifications
if [ ! -z "${ICE_PWD}" ];then
install_icecast
install_livestream_service
fi
if [[ "${INSTALL_NOMACHINE}" =~ [Yy] ]];then
install_nomachine
fi
create_necessary_dirs
generate_BirdDB
install_cleanup_cron
install_ftpd
install_lynx
}
if [ -f ${config_file} ];then
source ${config_file}
install_selected_services
else
echo "Unable to find a configuration file. Please make sure that $config_file exists."
fi
-38
View File
@@ -1,38 +0,0 @@
<?php
$timer=30;
header( "refresh:$timer;url=/advanced.php" );
?>
<html lang="en">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head></head>
<body style="background-color: rgb(119, 196, 135)">
<script>
function countDown(secs,elem) {
var element = document.getElementById(elem);
element.innerHTML = "Updating settings... Please allow another "+secs+" seconds for it to complete.";
if(secs < 1) {
clearTimeout(timer);
element.innerHTML = '<h4>Let\'s see</h4>';
}
secs--;
var timer = setTimeout('countDown('+secs+',"'+elem+'")',1000);
}
</script>
<div id="status"style="font-size:30px;"></div>
<script>countDown(<?php echo $timer;?>,"status");</script>
</body>
</html>
+1 -10
View File
@@ -1,14 +1,5 @@
#!/usr/bin/env bash
# Update the species list
#set -x
trap 'rm -f "$TMPFILE"' EXIT
source /etc/birdnet/birdnet.conf
db=birds
dbuser=birder
dbpassword=${DB_PWD}
mysql -u${dbuser} -p${dbpassword} ${db} \
-e 'SELECT Com_Name
FROM detections
GROUP BY Com_Name' |\
tail -n+2 > ${IDFILE}
sqlite3 /home/pi/BirdNET-Pi/scripts/birds.db "SELECT DISTINCT(Com_Name) FROM detections" | sort > ${IDFILE}
-123
View File
@@ -1,123 +0,0 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
header("refresh: 30;");
$mysqli = mysqli_connect();
$mysqli->select_db('birds');
if ($mysqli->connect_error) {
die('Connect Error (' .
$mysqli->connect_errno . ') '.
$mysqli->connect_error);
}
// SQL query to select data from database
$sql = "SELECT COUNT(*) AS 'Total' FROM detections
ORDER BY Date DESC, Time DESC";
$totalcount = $mysqli->query($sql);
$sql1 = "SELECT Date, Time, Sci_Name, Com_Name, MAX(Confidence)
FROM detections
WHERE Date = CURDATE()
GROUP BY Date, Time, Sci_Name, Com_Name
ORDER BY Time DESC";
$mosttable = $mysqli->query($sql1);
$sql2 = "SELECT COUNT(*) AS 'Total' FROM detections
WHERE Date = CURDATE()";
$todayscount = $mysqli->query($sql2);
$sql3 = "SELECT COUNT(*) AS 'Total' FROM detections
WHERE Date = CURDATE()
AND Time >= DATE_SUB(NOW(),INTERVAL 1 HOUR)";
$lasthourcount = $mysqli->query($sql3);
$sql4 = "SELECT Com_Name, Date, Time, MAX(Confidence)
FROM detections
GROUP BY Com_Name
ORDER BY MAX(Confidence) DESC";
$specieslist = $mysqli->query($sql4);
$speciescount = mysqli_num_rows($specieslist);
$sql5 = "SELECT Com_Name,COUNT(*)
AS 'Total'
FROM detections
GROUP BY Com_Name
ORDER BY Total DESC";
$speciestally = $mysqli->query($sql5);
$mysqli->close();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>BirdNET-Pi DB</title>
<link rel="stylesheet" href="style.css">
<style>
a {
text-decoration:none;
color:black;
}
.a2 { color:blue;}
</style>
</head>
<body style="background-color: rgb(119, 196, 135);">
<section>
<h2>Number of Detections</h2>
<div class="row">
<div class="column2">
<table>
<tr>
<th>Total</th>
<th>Today</th>
<th>Last Hour</th>
<th>Number of Unique Species</th>
</tr>
<tr>
<td><?php while ($row = $totalcount->fetch_assoc()) { echo $row['Total']; };?></td>
<td><?php while ($row = $todayscount->fetch_assoc()) { echo $row['Total']; };?></td>
<td><?php while ($row = $lasthourcount->fetch_assoc()) { echo $row['Total']; };?></td>
<td><?php echo $speciescount;?></td>
</tr>
</table>
</div>
</div>
<h2>Today's Detections</h2>
<!-- TABLE CONSTRUCTION-->
<table>
<tr>
<th>Time</th>
<th>Scientific Name</th>
<th>Common Name</th>
<th>Confidence</th>
<th>Links</th>
</tr>
<!-- PHP CODE TO FETCH DATA FROM ROWS-->
<?php // LOOP TILL END OF DATA
while($rows=$mosttable ->fetch_assoc())
{
$Confidence = sprintf("%.1f%%", $rows['MAX(Confidence)'] * 100);
$dbname = preg_replace('/ /', '_', $rows['Com_Name']);
$dbname = preg_replace('/\'/', '', $dbname);
$dbsciname = preg_replace('/ /', '_', $rows['Sci_Name']);
?>
<tr>
<!--FETCHING DATA FROM EACH
ROW OF EVERY COLUMN-->
<td><?php echo $rows['Time'];?></td>
<td><a href="/By_Scientific_Name/<?php echo $dbsciname;?>"/><?php echo $rows['Sci_Name'];?></a></td>
<td><a href="/By_Common_Name/<?php echo $dbname;?>"/><?php echo $rows['Com_Name'];?></a></td>
<td><?php echo $Confidence;?></td>
<td><a class="a2" href="https://allaboutbirds.org/guide/<?php echo $dbname;?>" target="top">All About Birds</a>, <a class="a2" href="https://wikipedia.org/wiki/<?php echo $dbsciname;?>" target="top">Wikipedia</a></td>
</tr>
<?php
}
?>
</table>
-167
View File
@@ -1,167 +0,0 @@
#!/usr/bin/env bash
# BirdNET-Pi Welcome Wiz!
#trap 'systemctl --user disable birdnet-pi-config-gui.service' EXIT 0
set -x
birdnet_config=/home/pi/BirdNET-Pi/birdnet.conf
zenity --title="Welcome!" --ok-label="Next" --window-icon=/usr/share/pixmaps/red-cardinal32.png \
--info --text="
Thank for you installing BirdNET-Pi\!" --no-wrap --icon-name=red-cardinal
[ $? -eq 1 ] && exit 1
overview() {
zenity --title="Configuration Wizard" --window-icon=/usr/share/pixmaps/red-cardinal32.png --width=300 --ok-label="Exit" \
--extra-button="Next" --info --text="This will walk you through the following steps:
1) Change the 'pi' user password
2) Setting your Language, Country, Timezone, Keyboard, and WiFi Country (if applicable)
3) Connect to WiFi (if applicable) / Set Date and Time (if applicable)
4) Set BirdNET-Pi's latitude and longitude
5) Set the web interface and database passwords
6) Updating the system to use your new birdnet.conf file" --no-wrap --icon-name=red-cardinal
if [ $? -eq 0 ];then
exit 0
elif [ $? -eq 1 ];then
open_rpi_configuration && change_password
fi
}
change_password() {
zenity --title="Change the 'pi' user password" \
--window-icon=/usr/share/pixmaps/red-cardinal32.png --ok-label="Back" \
--extra-button="Continue" --info --text="Click the button labeled \"Change Password...\"
and enter your new password in each box." --no-wrap --icon-name=red-cardinal
if [ $? -eq 1 ];then
change_locale
else
overview
fi
}
change_locale() {
zenity --title="Setting Language, Country, Timezone, Keyboard, and WiFI Country" \
--window-icon=/usr/share/pixmaps/red-cardinal32.png --ok-label="Back" \
--extra-button="Continue" --info --text="Use the right-most tab labeled \"Localisation\"
1) Locale: Set your Language and Country -- leave Character Set as \"UTF-8\"
2) Timezone: Set your Area and Location
3) Keyboard: Set your keyboard
4) WiFi Country: Choose your country from the list" --no-wrap --icon-name=red-cardinal
if [ $? -eq 1 ];then
sync_clock
else
change_password
fi
}
sync_clock() {
zenity --title="Connecting to WiFi/Setting the Date and Time" --window-icon=/usr/share/pixmaps/red-cardinal32.png \
--question --ok-label="Automatically" --cancel-label="Manually" --icon-name=red-cardinal --text="Should the installation update the date and time Automatically (using the internet),
or will you do this Manually?" --no-wrap
if [ $? -eq 1 ];then
get_date_and_time
else
setup_wifi
fi
}
get_date_and_time() {
full_date="$(zenity --forms --title="Set the current time" --window-icon=/usr/share/pixmaps/red-cardinal32.png --add-calendar=Calendar --add-combo="Current Hour (24h)" --combo-values="01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23" --add-combo="Current Minute" --combo-values="00|01|02|03|04|05|06|07|08|09|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" --add-combo="Current Second" --combo-values="00|01|02|03|04|05|06|07|08|09|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")"
current_date="$(echo ${full_date} | awk -F\| '{print $1}')"
if ! [ -z $current_date ];then
current_time="$(echo ${full_date} | awk -F\| '{print $1" "$2":"$3":"$4}')"
sudo date -s "${current_time}"
get_latitude
else
sync_clock
fi
}
setup_wifi() {
sudo systemctl start dhcpcd
wifi_list=""
until [[ "${wifi_list}" == "Continue" ]];do
wifi_list="$(zenity --title="Connect to WiFi" --extra-button="Refresh WiFi List" --window-icon=/usr/share/pixmaps/red-cardinal32.png --info --text="Use the Networking Icon in the bottom right corner of
the screen to select your WiFi network. Enter the credentials to connect when prompted.
If you are currently connected to BirdNET-Pi via its Access Point and VNC/NoMachine,
this connection will be severed as soon as the new connection is made.
Simply connect your VNC/NoMachine client to the network which you are
currently configuring BirdNET-Pi to use, and you will be able to resume the setup.
Press \"Continue\" AFTER you have connected." --ok-label="Continue" --no-wrap)"
[ -z ${wifi_list} ] && break
if [[ "${wifi_list}" == "Refresh WiFi List" ]];then
sudo systemctl restart dhcpcd
fi
done
get_latitude
}
open_rpi_configuration() {
if ! pgrep rc_gui;then
env SUDO_ASKPASS=/usr/lib/rc-gui/pwdrcg.sh sudo -AE rc_gui &
sleep 1.5
fi
}
get_latitude() {
new_lat="$(zenity --cancel-label="Back" --ok-label="Set" --title="Enter Your Latitude" --window-icon=/usr/share/pixmaps/red-cardinal32.png --width=300 --entry --text="Enter your Latitude.
A network guess: $(curl -s4 ifconfig.co/json | awk '/lat/ {print $2}' | tr -d ',')")"
if [ -n "$new_lat" ];then
get_longitude
else
change_locale
fi
}
get_longitude() {
new_lon="$(zenity --cancel-label="Back" --ok-label="Set" --title="Enter Your Longitude" --window-icon=/usr/share/pixmaps/red-cardinal32.png --width=300 --entry --text="Enter your Longitude.
A network guess: $(curl -s4 ifconfig.co/json | awk '/lon/ {print $2}' | tr -d ',')")"
if [ -n "$new_lon" ];then
set_caddy_pwd
else
get_latitude
fi
}
set_caddy_pwd() {
caddy_pwd="$(zenity --cancel-label="Back" --ok-label="Set" --title="Set Web Password" --window-icon=/usr/share/pixmaps/red-cardinal32.png --width=300 --entry --text="Set a new password for the web interface")"
if [ -n "${caddy_pwd}" ];then
set_db_pwd
else
get_longitude
fi
}
set_db_pwd() {
db_pwd="$(zenity --cancel-label="Back" --ok-label="Set" --title="Set The Database Password" --window-icon=/usr/share/pixmaps/red-cardinal32.png --width=300 --entry --text="Set a new password for the databse.")"
if [ -n "${db_pwd}" ];then
rearview
else
set_caddy_pwd
fi
}
overview
-50
View File
@@ -1,50 +0,0 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
$latitude = $_POST["latitude"];
$longitude = $_POST["longitude"];
$birdweather_id = $_POST["birdweather_id"];
$pushed_app_key = $_POST["pushed_app_key"];
$pushed_app_secret = $_POST["pushed_app_secret"];
$contents = file_get_contents("/home/pi/BirdNET-Pi/birdnet.conf");
$contents = preg_replace("/LATITUDE=.*/", "LATITUDE=$latitude", $contents);
$contents = preg_replace("/LONGITUDE=.*/", "LONGITUDE=$longitude", $contents);
$contents = preg_replace("/BIRDWEATHER_ID=.*/", "BIRDWEATHER_ID=$birdweather_id", $contents);
$contents = preg_replace("/PUSHED_APP_KEY=.*/", "PUSHED_APP_KEY=$pushed_app_key", $contents);
$contents = preg_replace("/PUSHED_APP_SECRET=.*/", "PUSHED_APP_SECRET=$pushed_app_secret", $contents);
$contents2 = file_get_contents("/home/pi/BirdNET-Pi/thisrun.txt");
$contents2 = preg_replace("/LATITUDE=.*/", "LATITUDE=$latitude", $contents2);
$contents2 = preg_replace("/LONGITUDE=.*/", "LONGITUDE=$longitude", $contents2);
$contents2 = preg_replace("/BIRDWEATHER_ID=.*/", "BIRDWEATHER_ID=$birdweather_id", $contents2);
$contents2 = preg_replace("/PUSHED_APP_KEY=.*/", "PUSHED_APP_KEY=$pushed_app_key", $contents2);
$contents2 = preg_replace("/PUSHED_APP_SECRET=.*/", "PUSHED_APP_SECRET=$pushed_app_secret", $contents2);
$fh = fopen("/home/pi/BirdNET-Pi/birdnet.conf", "w");
$fh2 = fopen("/home/pi/BirdNET-Pi/thisrun.txt", "w");
fwrite($fh, $contents);
fwrite($fh2, $contents2);
session_start();
$_SESSION['success'] = 1;
if(isset($_POST["normal"])){
header('Location:config.php');
}else{
header('Location:../../');
}
$language = $_POST["language"];
if ($language != "none"){
$command = "sudo -upi mv /home/pi/BirdNET-Pi/model/labels.txt /home/pi/BirdNET-Pi/model/labels.txt.old && sudo -upi unzip /home/pi/BirdNET-Pi/model/labels_l18n.zip $language -d /home/pi/BirdNET-Pi/model && sudo -upi mv /home/pi/BirdNET-Pi/model/$language /home/pi/BirdNET-Pi/model/labels.txt";
$command_output = `$command`;
session_start();
$_SESSION['success'] = 1;
if(isset($_POST["normal"])){
header('Location:config.php');
}else{
header('Location:../../');
}
}
?>
-10
View File
@@ -1,10 +0,0 @@
[Unit]
Description=Publish %I as alias for %H.local via mdns
After=network.target network-online.target
Requires=network-online.target
[Service]
Restart=always
Type=simple
ExecStart=/bin/bash -c "/usr/bin/avahi-publish -a -R %I $(avahi-resolve -4 -n %H.local | cut -f 2)"
[Install]
WantedBy=multi-user.target
+5
View File
@@ -112,3 +112,8 @@ if ! shopt -oq posix; then
fi
fi
cd ~/BirdNET-Pi
function remove() {
find /home/pi/BirdSongs -iname *${1}* -exec rm -drfv {} +
}
alias rerc="source ~/.bashrc"
alias vimrc="vim ~/.bashrc"
-14
View File
@@ -1,14 +0,0 @@
[Unit]
Description=BirdNET Analysis
Requires=${SYSTEMD_MOUNT}
After=network-online.target ${SYSTEMD_MOUNT}
[Service]
Restart=always
Type=simple
RestartSec=1
User=${BIRDNET_USER}
ExecStart=/usr/local/bin/birdnet_analysis.sh
[Install]
WantedBy=multi-user.target
-11
View File
@@ -1,11 +0,0 @@
[Unit]
Description=BirdNET Analysis Log
[Service]
Restart=on-failure
RestartSec=3
Type=simple
User=${BIRDNET_USER}
Environment=TERM=xterm-256color
ExecStart=/usr/local/bin/gotty -p 8080 --title-format "BirdNET-Lite Log" journalctl -fu birdnet_analysis.service
[Install]
WantedBy=multi-user.target
-11
View File
@@ -1,11 +0,0 @@
[Unit]
Description=BirdNET Recording
[Service]
Environment=XDG_RUNTIME_DIR=/run/user/1000
Restart=always
Type=simple
RestartSec=3
User=${BIRDNET_USER}
ExecStart=/usr/local/bin/birdnet_recording.sh
[Install]
WantedBy=multi-user.target
-11
View File
@@ -1,11 +0,0 @@
[Unit]
Description=BirdNET Statistics Log
[Service]
Restart=on-failure
RestartSec=3
Type=simple
User=${BIRDNET_USER}
Environment=TERM=xterm-256color
ExecStart=/usr/local/bin/gotty -p 9090 --title-format "BirdNET-Lite Statistics" /usr/local/bin/birdnet_stats.sh
[Install]
WantedBy=multi-user.target
-13
View File
@@ -1,13 +0,0 @@
[Unit]
Description=A BirdNET-Lite Web Terminal
[Service]
Restart=on-failure
RestartSec=3
Type=simple
User=${BIRDNET_USER}
Environment=TERM=xterm-256color
ExecStart=/usr/local/bin/gotty -w --title-format "Login!" -p 9111 tmux new -A -s Login sudo bash -c login
[Install]
WantedBy=multi-user.target
-10
View File
@@ -1,10 +0,0 @@
[Unit]
Description=BirdNET BirdSound Extraction
[Service]
Restart=on-failure
RestartSec=3
Type=simple
User=${BIRDNET_USER}
ExecStart=/usr/local/bin/extract_new_birdsounds.sh
[Install]
WantedBy=multi-user.target
-8
View File
@@ -1,8 +0,0 @@
[Unit]
Description=BirdNET BirdSound Extraction Timer
Requires=extraction.service
[Timer]
Unit=extraction.service
OnCalendar=*:*:0/10
[Install]
WantedBy=multi-user.target
-11
View File
@@ -1,11 +0,0 @@
[Unit]
Description=BirdNET Extraction Log
[Service]
Restart=on-failure
RestartSec=3
Type=simple
User=${BIRDNET_USER}
Environment=TERM=xterm-256color
ExecStart=/usr/local/bin/gotty -p 8888 --title-format "Extractions Log" journalctl -fu extraction.service
[Install]
WantedBy=multi-user.target
-11
View File
@@ -1,11 +0,0 @@
[Unit]
Description=BirdNET-Pi Live Stream
[Service]
Environment=XDG_RUNTIME_DIR=/run/user/1000
Restart=always
Type=simple
RestartSec=3
User=${BIRDNET_USER}
ExecStart=ffmpeg -loglevel 52 -ac 2 -f alsa -i ${REC_CARD} -acodec libmp3lame -b:a 320k -ac 2 -content_type 'audio/mpeg' -f mp3 icecast://source:${ICE_PWD}@localhost:8000/stream -re
[Install]
WantedBy=multi-user.target
+2 -2
View File
@@ -397,7 +397,7 @@ HIDE_TOTALS=false
; Example : HIDE_NETWORK_INTERFACE="eth0,sit0"
; HIDE_NETWORK_INTERFACE=true //hide all network interfaces
;
HIDE_NETWORK_INTERFACE="lo"
HIDE_NETWORK_INTERFACE=""
; Use a regular expression in the name of a hidden network interface (e.g. HIDE_NETWORK_INTERFACE="docker.*")
@@ -785,7 +785,7 @@ USE_REGEX=false
;
; string contains a list of process names that are checked, names are seperated by a comma (on WinNT names must end with '.exe')
;
PROCESSES="avahi-daemon,gotty,sshd,mariadbd,ffmpeg,vncserver-x11-serviced,nxd,bash"
PROCESSES="avahi-daemon,gotty,sshd,ffmpeg,icecast2,arecord,server.py"
[quotas]
-10
View File
@@ -1,10 +0,0 @@
[Unit]
Description=BirdNET-Pi Pushed.co Notifications
[Service]
Restart=on-success
RestartSec=3
Type=simple
User=pi
ExecStart=/usr/local/bin/species_notifier.sh
[Install]
WantedBy=multi-user.target
+13
View File
@@ -1,3 +1,16 @@
# 0.13
- Removed secondary and tertiary custom URLs
- Added new custom-compiled GoTTY binary
- All new web interface
- SQLite database replaced MariaDB
- Better service controls
- Spectrogram views
- Privacy server.py for testing
- New mobile view
- Moved (nearly) all CSS to style.css
- Added "try/except" to server.py to fail BirdWeather
POSTing gracefully and not kill the service
# main v.11.1
- `server.py` socket
- Removed DB password from scripts