This commit is contained in:
Your Name
2025-05-27 16:42:53 +02:00
parent caf5488b06
commit 381cf85336
18 changed files with 626 additions and 78 deletions

View File

@@ -29,7 +29,7 @@ Line by line installation.
``` ```
sudo apt update sudo apt update
sudo apt install git gh apache2 sqlite3 php php-sqlite3 python3 python3-pip jq autossh i2c-tools python3-smbus -y sudo apt install git gh apache2 sqlite3 php php-sqlite3 python3 python3-pip jq autossh i2c-tools python3-smbus -y
sudo pip3 install pyserial requests RPi.GPIO adafruit-circuitpython-bme280 crcmod psutil ntplib pytz gpiozero adafruit-circuitpython-ads1x15 numpy --break-system-packages sudo pip3 install pyserial requests RPi.GPIO adafruit-circuitpython-bme280 crcmod psutil ntplib pytz gpiozero adafruit-circuitpython-ads1x15 numpy nsrt-mk3-dev --break-system-packages
sudo mkdir -p /var/www/.ssh sudo mkdir -p /var/www/.ssh
sudo ssh-keygen -t rsa -b 4096 -f /var/www/.ssh/id_rsa -N "" sudo ssh-keygen -t rsa -b 4096 -f /var/www/.ssh/id_rsa -N ""
sudo ssh-copy-id -i /var/www/.ssh/id_rsa.pub -p 50221 airlab_server1@aircarto.fr sudo ssh-copy-id -i /var/www/.ssh/id_rsa.pub -p 50221 airlab_server1@aircarto.fr

View File

@@ -111,6 +111,13 @@
</label> </label>
</div> </div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" value="" id="check_NOISE" onchange="update_config_sqlite('NOISE', this.checked)">
<label class="form-check-label" for="check_NOISE">
Send Noise data
</label>
</div>
<div class="form-check mb-3"> <div class="form-check mb-3">
<input class="form-check-input" type="checkbox" value="" id="check_uSpot" onchange="update_config_sqlite('send_uSpot', this.checked)" disabled> <input class="form-check-input" type="checkbox" value="" id="check_uSpot" onchange="update_config_sqlite('send_uSpot', this.checked)" disabled>
<label class="form-check-label" for="check_uSpot"> <label class="form-check-label" for="check_uSpot">
@@ -361,12 +368,15 @@ window.onload = function() {
const checkbox_bme = document.getElementById("check_bme280"); const checkbox_bme = document.getElementById("check_bme280");
const checkbox_envea = document.getElementById("check_envea"); const checkbox_envea = document.getElementById("check_envea");
const checkbox_solar = document.getElementById("check_solarBattery"); const checkbox_solar = document.getElementById("check_solarBattery");
const checkbox_noise = document.getElementById("check_NOISE");
checkbox_bme.checked = response["BME280"]; checkbox_bme.checked = response["BME280"];
checkbox_envea.checked = response["envea"]; checkbox_envea.checked = response["envea"];
checkbox_solar.checked = response["MPPT"]; checkbox_solar.checked = response["MPPT"];
checkbox_nmp5channels.checked = response.npm_5channel; checkbox_nmp5channels.checked = response.npm_5channel;
checkbox_wind.checked = response["windMeter"]; checkbox_wind.checked = response["windMeter"];
checkbox_noise.checked = response["NOISE"];
checkbox_uSpot.checked = response["send_uSpot"]; checkbox_uSpot.checked = response["send_uSpot"];
// If envea is enabled, show the envea sondes container // If envea is enabled, show the envea sondes container

View File

@@ -71,6 +71,8 @@
<button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',getSelectedLimit(),false)">Mesures Temp/Hum</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',getSelectedLimit(),false)">Mesures Temp/Hum</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',getSelectedLimit(),false)">Mesures PM (5 canaux)</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',getSelectedLimit(),false)">Mesures PM (5 canaux)</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_envea',getSelectedLimit(),false)">Sonde Cairsens</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_envea',getSelectedLimit(),false)">Sonde Cairsens</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_NOISE',getSelectedLimit(),false)">Sonde bruit</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_WIND',getSelectedLimit(),false)">Sonde Vent</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_WIND',getSelectedLimit(),false)">Sonde Vent</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_MPPT',getSelectedLimit(),false)">Batterie</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_MPPT',getSelectedLimit(),false)">Batterie</button>
@@ -97,6 +99,8 @@
<button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',10,true, getStartDate(), getEndDate())">Mesures Temp/Hum</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_BME280',10,true, getStartDate(), getEndDate())">Mesures Temp/Hum</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',10,true, getStartDate(), getEndDate())">Mesures PM (5 canaux)</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_NPM_5channels',10,true, getStartDate(), getEndDate())">Mesures PM (5 canaux)</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_envea',10,true, getStartDate(), getEndDate())">Sonde Cairsens</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_envea',10,true, getStartDate(), getEndDate())">Sonde Cairsens</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_NOISE',10,true, getStartDate(), getEndDate())">Sonde Bruit</button>
<button class="btn btn-primary" onclick="get_data_sqlite('data_mppt',10,true, getStartDate(), getEndDate())">Batterie</button> <button class="btn btn-primary" onclick="get_data_sqlite('data_mppt',10,true, getStartDate(), getEndDate())">Batterie</button>
</table> </table>
@@ -291,10 +295,17 @@ function get_data_sqlite(table, limit, download , startDate = "", endDate = "")
<th> solar_power</th> <th> solar_power</th>
<th> charger_status</th> <th> charger_status</th>
`;
}else if (table === "data_NOISE") {
tableHTML += `
<th>Timestamp</th>
<th>Curent LEQ</th>
<th>DB_A_value</th>
`; `;
} }
tableHTML += `</tr></thead><tbody>`; tableHTML += `</tr></thead><tbody>`;
// Loop through rows and create table rows // Loop through rows and create table rows
@@ -357,6 +368,12 @@ function get_data_sqlite(table, limit, download , startDate = "", endDate = "")
<td>${columns[3]}</td> <td>${columns[3]}</td>
<td>${columns[4]}</td> <td>${columns[4]}</td>
<td>${columns[5]}</td> <td>${columns[5]}</td>
`;
}else if (table === "data_NOISE") {
tableHTML += `
<td>${columns[0]}</td>
<td>${columns[1]}</td>
<td>${columns[2]}</td>
`; `;
} }

View File

@@ -1017,6 +1017,10 @@ if ($type == "get_systemd_services") {
'description' => 'Tracks solar panel and battery status', 'description' => 'Tracks solar panel and battery status',
'frequency' => 'Every 2 minutes' 'frequency' => 'Every 2 minutes'
], ],
'nebuleair-noise-data.timer' => [
'description' => 'Get Data from noise sensor',
'frequency' => 'Every minute'
],
'nebuleair-db-cleanup-data.timer' => [ 'nebuleair-db-cleanup-data.timer' => [
'description' => 'Cleans up old data from database', 'description' => 'Cleans up old data from database',
'frequency' => 'Daily' 'frequency' => 'Daily'

View File

@@ -27,7 +27,7 @@ sudo apt update && sudo apt install -y git gh apache2 sqlite3 php php-sqlite3 py
# Install Python libraries # Install Python libraries
info "Installing Python libraries..." info "Installing Python libraries..."
sudo pip3 install pyserial requests RPi.GPIO adafruit-circuitpython-bme280 crcmod psutil gpiozero ntplib pytz --break-system-packages || error "Failed to install Python libraries." sudo pip3 install pyserial requests RPi.GPIO adafruit-circuitpython-bme280 crcmod psutil gpiozero ntplib adafruit-circuitpython-ads1x15 nsrt-mk3-dev pytz --break-system-packages || error "Failed to install Python libraries."
# Clone the repository (check if it exists first) # Clone the repository (check if it exists first)
REPO_DIR="/var/www/nebuleair_pro_4g" REPO_DIR="/var/www/nebuleair_pro_4g"

View File

@@ -1,39 +1,277 @@
#!/bin/bash #!/bin/bash
# File: /var/www/nebuleair_pro_4g/services/check_services.sh # File: /var/www/nebuleair_pro_4g/services/check_services.sh
# Purpose: Check status of all NebuleAir services and logs # Purpose: Check status of all NebuleAir services and logs
# Install: # Version with fixed color handling for proper table display
# sudo chmod +x /var/www/nebuleair_pro_4g/services/check_services.sh
# sudo /var/www/nebuleair_pro_4g/services/check_services.sh
echo "=== NebuleAir Services Status ===" # Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m' # No Color
# Service list
SERVICES=("npm" "envea" "sara" "bme280" "mppt" "db-cleanup" "noise")
# Function to print header
print_header() {
local text="$1"
echo "" echo ""
echo -e "${BLUE}${BOLD}=== $text ===${NC}"
echo -e "${BLUE}$(printf '%.0s=' {1..70})${NC}"
}
# Check status of all timers # Function to print section
echo "--- TIMER STATUS ---" print_section() {
systemctl list-timers | grep nebuleair local text="$1"
echo "" echo ""
echo -e "${CYAN}${BOLD}--- $text ---${NC}"
}
# Check status of all services # Function to print a separator line
echo "--- SERVICE STATUS ---" print_separator() {
for service in npm envea sara bme280 mppt db-cleanup; do echo "+--------------------------+-----------+-----------+-------------+-------------+-------------------------+"
status=$(systemctl is-active nebuleair-$service-data.service) }
timer_status=$(systemctl is-active nebuleair-$service-data.timer)
echo "nebuleair-$service-data: Service=$status, Timer=$timer_status" # Clear screen for clean output
clear
# Main header
print_header "NebuleAir Services Status Report"
echo -e "Generated on: $(date '+%Y-%m-%d %H:%M:%S')"
# Timer Schedule
print_section "Active Timers Schedule"
echo ""
systemctl list-timers --no-pager | head -n 1
systemctl list-timers --no-pager | grep nebuleair || echo "No active nebuleair timers found"
# Service Status Overview with fixed color handling
print_section "Service Status Overview"
echo ""
print_separator
printf "| %-24s | %-9s | %-9s | %-11s | %-11s | %-23s |\n" "Service" "Svc State" "Svc Boot" "Timer State" "Timer Boot" "Health Status"
print_separator
for service in "${SERVICES[@]}"; do
# Check the actual service and timer names (with -data suffix)
full_service_name="nebuleair-${service}-data"
# Get raw status values
service_status=$(systemctl is-active ${full_service_name}.service 2>/dev/null | tr -d '\n' || echo "not-found")
service_enabled=$(systemctl is-enabled ${full_service_name}.service 2>/dev/null | tr -d '\n' || echo "not-found")
timer_status=$(systemctl is-active ${full_service_name}.timer 2>/dev/null | tr -d '\n' || echo "not-found")
timer_enabled=$(systemctl is-enabled ${full_service_name}.timer 2>/dev/null | tr -d '\n' || echo "not-found")
# Check if files exist and override if not found
if ! systemctl list-unit-files | grep -q "^${full_service_name}.service" &>/dev/null; then
service_status="not-found"
service_enabled="not-found"
fi
if ! systemctl list-unit-files | grep -q "^${full_service_name}.timer" &>/dev/null; then
timer_status="not-found"
timer_enabled="not-found"
fi
# Create display strings without embedded colors for table cells
case $service_status in
"active") svc_st_display="active"; svc_st_color="${GREEN}" ;;
"inactive") svc_st_display="inactive"; svc_st_color="${DIM}" ;;
"activating") svc_st_display="starting"; svc_st_color="${YELLOW}" ;;
"not-found") svc_st_display="missing"; svc_st_color="${RED}" ;;
*) svc_st_display="$service_status"; svc_st_color="${RED}" ;;
esac
case $service_enabled in
"enabled"|"static") svc_en_display="enabled"; svc_en_color="${GREEN}" ;;
"disabled") svc_en_display="disabled"; svc_en_color="${YELLOW}" ;;
"not-found") svc_en_display="missing"; svc_en_color="${RED}" ;;
*) svc_en_display="$service_enabled"; svc_en_color="${YELLOW}" ;;
esac
case $timer_status in
"active") tim_st_display="active"; tim_st_color="${GREEN}" ;;
"inactive") tim_st_display="inactive"; tim_st_color="${RED}" ;;
"not-found") tim_st_display="missing"; tim_st_color="${RED}" ;;
*) tim_st_display="$timer_status"; tim_st_color="${RED}" ;;
esac
case $timer_enabled in
"enabled"|"static") tim_en_display="enabled"; tim_en_color="${GREEN}" ;;
"disabled") tim_en_display="disabled"; tim_en_color="${YELLOW}" ;;
"not-found") tim_en_display="missing"; tim_en_color="${RED}" ;;
*) tim_en_display="$timer_enabled"; tim_en_color="${YELLOW}" ;;
esac
# Determine health status
if [[ "$timer_status" == "active" ]]; then
if [[ "$timer_enabled" == "enabled" || "$timer_enabled" == "static" ]]; then
health_display="✓ OK"
health_color="${GREEN}"
else
health_display="⚠ Boot disabled"
health_color="${YELLOW}"
fi
elif [[ "$timer_status" == "inactive" ]]; then
health_display="✗ Timer stopped"
health_color="${RED}"
else
health_display="✗ Timer missing"
health_color="${RED}"
fi
# Print row with colors applied outside of printf formatting
printf "| %-24s | " "$full_service_name"
printf "${svc_st_color}%-9s${NC} | " "$svc_st_display"
printf "${svc_en_color}%-9s${NC} | " "$svc_en_display"
printf "${tim_st_color}%-11s${NC} | " "$tim_st_display"
printf "${tim_en_color}%-11s${NC} | " "$tim_en_display"
printf "${health_color}%-23s${NC} |\n" "$health_display"
done done
echo "" print_separator
# Show recent logs for each service # Understanding the table
echo "--- RECENT LOGS (last 5 entries per service) ---"
for service in npm envea sara bme280 mppt db-cleanup; do
echo "[$service service logs]"
journalctl -u nebuleair-$service-data.service -n 5 --no-pager
echo "" echo ""
echo -e "${DIM}Note: For timer-based services, it's normal for the service to be 'inactive' and 'disabled'.${NC}"
echo -e "${DIM} What matters is that the timer is 'active' and 'enabled'.${NC}"
# Configuration Issues
print_section "Configuration Issues"
echo ""
issues_found=false
for service in "${SERVICES[@]}"; do
full_service_name="nebuleair-${service}-data"
timer_status=$(systemctl is-active ${full_service_name}.timer 2>/dev/null | tr -d '\n' || echo "not-found")
timer_enabled=$(systemctl is-enabled ${full_service_name}.timer 2>/dev/null | tr -d '\n' || echo "not-found")
# Check if timer exists
if ! systemctl list-unit-files | grep -q "^${full_service_name}.timer" &>/dev/null; then
timer_status="not-found"
timer_enabled="not-found"
fi
if [[ "$timer_status" != "active" || ("$timer_enabled" != "enabled" && "$timer_enabled" != "static") ]]; then
issues_found=true
echo -e " ${RED}${NC} ${BOLD}$full_service_name${NC}"
if [[ "$timer_status" == "not-found" ]]; then
echo -e " ${RED}${NC} Timer unit file is missing"
elif [[ "$timer_status" != "active" ]]; then
echo -e " ${RED}${NC} Timer is not running (status: $timer_status)"
fi
if [[ "$timer_enabled" == "not-found" ]]; then
echo -e " ${RED}${NC} Timer unit file is missing"
elif [[ "$timer_enabled" != "enabled" && "$timer_enabled" != "static" ]]; then
echo -e " ${YELLOW}${NC} Timer won't start on boot (status: $timer_enabled)"
fi
echo ""
fi
done done
echo "=== End of Report ===" if [[ "$issues_found" == "false" ]]; then
echo -e " ${GREEN}${NC} All timers are properly configured!"
fi
# Recent Executions - Simplified
print_section "Last Execution Status"
echo ""
printf " %-12s %-20s %s\n" "Service" "Last Run" "Status"
printf " %-12s %-20s %s\n" "-------" "--------" "------"
for service in "${SERVICES[@]}"; do
full_service_name="nebuleair-${service}-data"
# Get last execution time and status
last_log=$(journalctl -u ${full_service_name}.service -n 3 --no-pager 2>/dev/null | grep -E "(Started|Finished|Failed)" | tail -1)
if [[ -n "$last_log" ]]; then
timestamp=$(echo "$last_log" | awk '{print $1, $2, $3}')
if echo "$last_log" | grep -q "Finished"; then
status="${GREEN}✓ Success${NC}"
elif echo "$last_log" | grep -q "Failed"; then
status="${RED}✗ Failed${NC}"
elif echo "$last_log" | grep -q "Started"; then
status="${YELLOW}⟳ Running${NC}"
else
status="${DIM}- Unknown${NC}"
fi
printf " %-12s %-20s %b\n" "$service" "$timestamp" "$status"
else
printf " %-12s %-20s %b\n" "$service" "-" "${DIM}- No data${NC}"
fi
done
# Summary
print_section "Summary"
echo ""
working=0
needs_attention=0
for service in "${SERVICES[@]}"; do
full_service_name="nebuleair-${service}-data"
timer_status=$(systemctl is-active ${full_service_name}.timer 2>/dev/null | tr -d '\n')
timer_enabled=$(systemctl is-enabled ${full_service_name}.timer 2>/dev/null | tr -d '\n')
if [[ "$timer_status" == "active" ]] && [[ "$timer_enabled" == "enabled" || "$timer_enabled" == "static" ]]; then
((working++))
else
((needs_attention++))
fi
done
total=${#SERVICES[@]}
# Visual progress bar
echo -n " Overall Health: ["
for ((i=1; i<=10; i++)); do
if ((i <= working * 10 / total)); then
echo -n -e "${GREEN}${NC}"
else
echo -n -e "${RED}${NC}"
fi
done
echo -e "] ${working}/${total}"
echo ""
echo -e " ${GREEN}${NC} Working properly: ${BOLD}$working${NC} services"
echo -e " ${RED}${NC} Need attention: ${BOLD}$needs_attention${NC} services"
# Quick Commands
print_section "Quick Commands"
echo ""
echo -e " ${BOLD}Fix a timer that needs attention:${NC}"
echo " $ sudo systemctl enable --now nebuleair-[service]-data.timer"
echo ""
echo -e " ${BOLD}View live logs:${NC}"
echo " $ sudo journalctl -u nebuleair-[service]-data.service -f"
echo ""
echo -e " ${BOLD}Check timer details:${NC}"
echo " $ systemctl status nebuleair-[service]-data.timer"
echo ""
echo -e " ${BOLD}Run service manually:${NC}"
echo " $ sudo systemctl start nebuleair-[service]-data.service"
# Specific fixes needed
if [[ $needs_attention -gt 0 ]]; then
echo ""
echo -e "${YELLOW}${BOLD}Recommended Actions:${NC}"
for service in "${SERVICES[@]}"; do
full_service_name="nebuleair-${service}-data"
timer_status=$(systemctl is-active ${full_service_name}.timer 2>/dev/null | tr -d '\n')
timer_enabled=$(systemctl is-enabled ${full_service_name}.timer 2>/dev/null | tr -d '\n')
if [[ "$timer_status" != "active" ]] && [[ "$timer_status" != "not-found" ]]; then
echo -e " ${RED}${NC} sudo systemctl start ${full_service_name}.timer"
fi
if [[ "$timer_enabled" != "enabled" ]] && [[ "$timer_enabled" != "static" ]] && [[ "$timer_enabled" != "not-found" ]]; then
echo -e " ${YELLOW}${NC} sudo systemctl enable ${full_service_name}.timer"
fi
done
fi
echo "" echo ""
echo "For detailed logs use:"
echo " sudo journalctl -u nebuleair-[service]-data.service -f"
echo "To restart a specific service timer:"
echo " sudo systemctl restart nebuleair-[service]-data.timer"

View File

@@ -173,6 +173,38 @@ AccuracySec=1s
WantedBy=timers.target WantedBy=timers.target
EOL EOL
# Create service and timer files for noise Data (every minutes)
cat > /etc/systemd/system/nebuleair-noise-data.service << 'EOL'
[Unit]
Description=NebuleAir noise Data Collection Service
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /var/www/nebuleair_pro_4g/sound_meter/NSRT_mk4_get_data.py
User=root
WorkingDirectory=/var/www/nebuleair_pro_4g
StandardOutput=append:/var/www/nebuleair_pro_4g/logs/noise_service.log
StandardError=append:/var/www/nebuleair_pro_4g/logs/noise_service_errors.log
[Install]
WantedBy=multi-user.target
EOL
cat > /etc/systemd/system/nebuleair-noise-data.timer << 'EOL'
[Unit]
Description=Run NebuleAir MPPT Data Collection every 120 seconds
Requires=nebuleair-noise-data.service
[Timer]
OnBootSec=60s
OnUnitActiveSec=60s
AccuracySec=1s
[Install]
WantedBy=timers.target
EOL
# Create service and timer files for Database Cleanup # Create service and timer files for Database Cleanup
cat > /etc/systemd/system/nebuleair-db-cleanup-data.service << 'EOL' cat > /etc/systemd/system/nebuleair-db-cleanup-data.service << 'EOL'
[Unit] [Unit]
@@ -210,7 +242,7 @@ systemctl daemon-reload
# Enable and start all timers # Enable and start all timers
echo "Enabling and starting all services..." echo "Enabling and starting all services..."
for service in npm envea sara bme280 mppt db-cleanup; do for service in npm envea sara bme280 mppt db-cleanup noise; do
systemctl enable nebuleair-$service-data.timer systemctl enable nebuleair-$service-data.timer
systemctl start nebuleair-$service-data.timer systemctl start nebuleair-$service-data.timer
echo "Started nebuleair-$service-data timer" echo "Started nebuleair-$service-data timer"

View File

@@ -0,0 +1,39 @@
'''
____ ___ _ _ _ _ ____
/ ___| / _ \| | | | \ | | _ \
\___ \| | | | | | | \| | | | |
___) | |_| | |_| | |\ | |_| |
|____/ \___/ \___/|_| \_|____/
python3 /var/www/nebuleair_pro_4g/sound_meter/NSRT_MK4_change_config.py
1.Intervalle d'enregistrement
L'intervalle d'enregistrement définit le temps entre deux points successifs enregistrés.
Cela définit également la période d'intégration pour le LEQ, et la période d'observation pour L-min et L-max et Lpeak.
L'intervalle d'enregistrement peut être réglé de 125 ms (1/8ème) à 2 H par incréments de 125 ms.
some parameters can be changed:
write_tau(tau: float) -> time constant
write_fs(frequency: int) -> sampling freq
'''
import nsrt_mk3_dev
nsrt = nsrt_mk3_dev.NsrtMk3Dev('/dev/ttyACM0')
#####################
#change time constant
nsrt.write_tau(60)
#####################
freq_level = nsrt.read_fs() #current sampling frequency
time_constant = nsrt.read_tau() #reads the current time constant
leq_level = nsrt.read_leq() #current running LEQ and starts the integration of a new LEQ.
weighting = nsrt.read_weighting() #weighting curve that is currently selected
weighted_level = nsrt.read_level() #current running level in dB.
print(f'current sampling freq : {freq_level} Hz')
print(f'current time constant : {time_constant} s')
print(f'current LEQ level: {leq_level:0.2f} dB')
print(f'{weighting} value: {weighted_level:0.2f} dBA')

View File

@@ -0,0 +1,69 @@
'''
____ ___ _ _ _ _ ____
/ ___| / _ \| | | | \ | | _ \
\___ \| | | | | | | \| | | | |
___) | |_| | |_| | |\ | |_| |
|____/ \___/ \___/|_| \_|____/
python3 /var/www/nebuleair_pro_4g/sound_meter/get_data.py
Script to get data from the NSRT_MK4 Sound Level Meter
Need to install "nsrt_mk3_dev"
1.Intervalle d'enregistrement
L'intervalle d'enregistrement définit le temps entre deux points successifs enregistrés.
Cela définit également la période d'intégration pour le LEQ, et la période d'observation pour L-min et L-max et Lpeak.
L'intervalle d'enregistrement peut être réglé de 125 ms (1/8ème) à 2 H par incréments de 125 ms.
some parameters can be changed:
write_tau(tau: float) -> time constant
write_fs(frequency: int) -> sampling freq
'''
import nsrt_mk3_dev
import sqlite3
nsrt = nsrt_mk3_dev.NsrtMk3Dev('/dev/ttyACM0')
# Connect to the SQLite database
conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
cursor = conn.cursor()
#GET RTC TIME from SQlite
cursor.execute("SELECT * FROM timestamp_table LIMIT 1")
row = cursor.fetchone() # Get the first (and only) row
rtc_time_str = row[1] # '2025-02-07 12:30:45'
freq_level = nsrt.read_fs() #current sampling frequency
time_constant = nsrt.read_tau() #reads the current time constant
leq_level = nsrt.read_leq() #current running LEQ and starts the integration of a new LEQ.
weighting = nsrt.read_weighting() #weighting curve that is currently selected
weighted_level = nsrt.read_level() #current running level in dB.
#print(f'current sampling freq : {freq_level} Hz')
#print(f'current time constant : {time_constant} s')
#print(f'current LEQ level: {leq_level:0.2f} dB')
#print(f'{weighting} value: {weighted_level:0.2f} dBA')
# Round values to 2 decimal places before saving
leq_level_rounded = round(leq_level, 2)
weighted_level_rounded = round(weighted_level, 2)
#save to db
#save to sqlite database
try:
cursor.execute('''
INSERT INTO data_NOISE (timestamp,current_LEQ, DB_A_value) VALUES (?,?,?)'''
, (rtc_time_str,leq_level_rounded,weighted_level_rounded))
# Commit and close the connection
conn.commit()
#print("Sensor data saved successfully!")
except Exception as e:
print(f"Database error: {e}")
conn.close()

View File

View File

View File

@@ -127,6 +127,14 @@ CREATE TABLE IF NOT EXISTS data_MPPT (
) )
""") """)
# Create a table noise capture (NSRT mk4)
cursor.execute("""
CREATE TABLE IF NOT EXISTS data_NOISE (
timestamp TEXT,
current_LEQ REAL,
DB_A_value REAL
)
""")
# Commit and close the connection # Commit and close the connection
conn.commit() conn.commit()

View File

@@ -9,6 +9,9 @@ Script to flush (delete) data from a sqlite database
/usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/flush_old_data.py /usr/bin/python3 /var/www/nebuleair_pro_4g/sqlite/flush_old_data.py
Script that is triggered by a systemd
sudo systemctl status nebuleair-db-cleanup-data.service
Available table are Available table are
data_NPM data_NPM
@@ -17,56 +20,183 @@ data_BME280
data_envea data_envea
timestamp_table timestamp_table
data_MPPT data_MPPT
data_NOISE
data_WIND
''' '''
import sqlite3 import sqlite3
import datetime import datetime
import sys
def table_exists(cursor, table_name):
"""Check if a table exists in the database"""
try:
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,))
return cursor.fetchone() is not None
except sqlite3.Error as e:
print(f"[ERROR] Failed to check if table '{table_name}' exists: {e}")
return False
def get_table_count(cursor, table_name):
"""Get the number of records in a table"""
try:
cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
return cursor.fetchone()[0]
except sqlite3.Error as e:
print(f"[WARNING] Could not get count for table '{table_name}': {e}")
return 0
def delete_old_records(cursor, table_name, cutoff_date_str):
"""Delete old records from a specific table"""
try:
# First check how many records will be deleted
cursor.execute(f"SELECT COUNT(*) FROM {table_name} WHERE timestamp < ?", (cutoff_date_str,))
records_to_delete = cursor.fetchone()[0]
if records_to_delete == 0:
print(f"[INFO] No old records to delete from '{table_name}'")
return True
# Delete the records
cursor.execute(f"DELETE FROM {table_name} WHERE timestamp < ?", (cutoff_date_str,))
deleted_count = cursor.rowcount
print(f"[SUCCESS] Deleted {deleted_count} old records from '{table_name}'")
return True
except sqlite3.Error as e:
print(f"[ERROR] Failed to delete records from '{table_name}': {e}")
return False
def main():
try:
# Connect to the SQLite database # Connect to the SQLite database
print("[INFO] Connecting to database...")
conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db") conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
cursor = conn.cursor() cursor = conn.cursor()
#GET RTC TIME from SQlite # Check database connection
cursor.execute("SELECT * FROM timestamp_table LIMIT 1") cursor.execute("SELECT sqlite_version()")
row = cursor.fetchone() # Get the first (and only) row version = cursor.fetchone()[0]
print(f"[INFO] Connected to SQLite version: {version}")
# GET RTC TIME from SQLite
print("[INFO] Getting timestamp from database...")
# First check if timestamp_table exists
if not table_exists(cursor, "timestamp_table"):
print("[ERROR] timestamp_table does not exist!")
return False
cursor.execute("SELECT * FROM timestamp_table LIMIT 1")
row = cursor.fetchone()
if not row:
print("[ERROR] No timestamp found in timestamp_table.")
return False
if row:
rtc_time_str = row[1] # Assuming timestamp is stored as TEXT (YYYY-MM-DD HH:MM:SS) rtc_time_str = row[1] # Assuming timestamp is stored as TEXT (YYYY-MM-DD HH:MM:SS)
print(f"[INFO] Last recorded timestamp: {rtc_time_str}") print(f"[INFO] Last recorded timestamp: {rtc_time_str}")
# Convert last_updated to a datetime object # Convert last_updated to a datetime object
try:
last_updated = datetime.datetime.strptime(rtc_time_str, "%Y-%m-%d %H:%M:%S") last_updated = datetime.datetime.strptime(rtc_time_str, "%Y-%m-%d %H:%M:%S")
except ValueError as e:
print(f"[ERROR] Invalid timestamp format: {e}")
return False
# Calculate the cutoff date (3 months before last_updated) # Calculate the cutoff date (60 days before last_updated)
cutoff_date = last_updated - datetime.timedelta(days=60) cutoff_date = last_updated - datetime.timedelta(days=60)
cutoff_date_str = cutoff_date.strftime("%Y-%m-%d %H:%M:%S") cutoff_date_str = cutoff_date.strftime("%Y-%m-%d %H:%M:%S")
print(f"[INFO] Deleting records older than: {cutoff_date_str}") print(f"[INFO] Deleting records older than: {cutoff_date_str}")
# List of tables to delete old data from # List of tables to delete old data from
tables_to_clean = ["data_NPM", "data_NPM_5channels", "data_BME280", "data_envea","data_WIND", "data_MPPT"] tables_to_clean = [
"data_NPM",
"data_NPM_5channels",
"data_BME280",
"data_envea",
"data_WIND",
"data_MPPT",
"data_NOISE"
]
# Check which tables actually exist
existing_tables = []
missing_tables = []
# Loop through each table and delete old data
for table in tables_to_clean: for table in tables_to_clean:
delete_query = f"DELETE FROM {table} WHERE timestamp < ?" if table_exists(cursor, table):
cursor.execute(delete_query, (cutoff_date_str,)) existing_tables.append(table)
print(f"[INFO] Deleted old records from {table}") record_count = get_table_count(cursor, table)
print(f"[INFO] Table '{table}' exists with {record_count} records")
# **Commit changes before running VACUUM**
conn.commit()
print("[INFO] Changes committed successfully!")
# Now it's safe to run VACUUM
print("[INFO] Running VACUUM to optimize database space...")
cursor.execute("VACUUM")
print("[SUCCESS] Old data flushed successfully!")
else: else:
print("[ERROR] No timestamp found in timestamp_table.") missing_tables.append(table)
print(f"[WARNING] Table '{table}' does not exist - skipping")
if missing_tables:
print(f"[INFO] Missing tables: {', '.join(missing_tables)}")
# Close the database connection if not existing_tables:
print("[WARNING] No tables found to clean!")
return True
# Loop through existing tables and delete old data
successful_deletions = 0
failed_deletions = 0
for table in existing_tables:
if delete_old_records(cursor, table, cutoff_date_str):
successful_deletions += 1
else:
failed_deletions += 1
# Commit changes before running VACUUM
print("[INFO] Committing changes...")
conn.commit()
print("[SUCCESS] Changes committed successfully!")
# Only run VACUUM if at least some deletions were successful
if successful_deletions > 0:
print("[INFO] Running VACUUM to optimize database space...")
try:
cursor.execute("VACUUM")
print("[SUCCESS] Database optimized successfully!")
except sqlite3.Error as e:
print(f"[WARNING] VACUUM failed: {e}")
# Summary
print(f"\n[SUMMARY]")
print(f"Tables processed successfully: {successful_deletions}")
print(f"Tables with errors: {failed_deletions}")
print(f"Tables skipped (missing): {len(missing_tables)}")
if failed_deletions == 0:
print("[SUCCESS] Old data flushed successfully!")
return True
else:
print("[WARNING] Some operations failed - check logs above")
return False
except sqlite3.Error as e:
print(f"[ERROR] Database error: {e}")
return False
except Exception as e:
print(f"[ERROR] Unexpected error: {e}")
return False
finally:
# Always close the database connection
if 'conn' in locals():
conn.close() conn.close()
print("[INFO] Database connection closed")
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)

View File

@@ -47,6 +47,7 @@ config_entries = [
("windMeter", "0", "bool"), ("windMeter", "0", "bool"),
("BME280", "0", "bool"), ("BME280", "0", "bool"),
("MPPT", "0", "bool"), ("MPPT", "0", "bool"),
("NOISE", "0", "bool"),
("modem_version", "XXX", "str") ("modem_version", "XXX", "str")
] ]