Update
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import json
|
import json
|
||||||
import threading
|
import threading
|
||||||
|
import requests
|
||||||
import paho.mqtt.client as mqtt
|
import paho.mqtt.client as mqtt
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
@@ -41,6 +42,11 @@ DATA_FILE = "/etc/frigate-counter/karung-tuang/karung_tuang.json"
|
|||||||
# =====================
|
# =====================
|
||||||
DB_FILE = "/etc/frigate-counter/karung-tuang/karung_tuang.db"
|
DB_FILE = "/etc/frigate-counter/karung-tuang/karung_tuang.db"
|
||||||
|
|
||||||
|
DETIK_ANTAR_KARUNG = 30
|
||||||
|
|
||||||
|
RELAY_KIRI_ON = "http://192.168.192.26:5001/relay_on"
|
||||||
|
RELAY_KIRI_OFF = "http://192.168.192.26:5001/relay_off"
|
||||||
|
|
||||||
|
|
||||||
def init_database():
|
def init_database():
|
||||||
"""Initialize the SQLite database with the required table"""
|
"""Initialize the SQLite database with the required table"""
|
||||||
@@ -138,13 +144,34 @@ def republish_counter():
|
|||||||
f"{SITE_TOPIC}/{camera}/{label}", value, qos=1, retain=True
|
f"{SITE_TOPIC}/{camera}/{label}", value, qos=1, retain=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# RELAY FUNCTION
|
||||||
|
# =====================
|
||||||
|
def relay_kiri_off():
|
||||||
|
global RELAY_KIRI_ON, RELAY_KIRI_OFF
|
||||||
|
try:
|
||||||
|
requests.get(RELAY_KIRI_OFF, timeout=1)
|
||||||
|
print(f"Relay Kiri OFF")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
def relay_kiri_on():
|
||||||
|
global DETIK_ANTAR_KARUNG
|
||||||
|
|
||||||
|
try:
|
||||||
|
requests.get(RELAY_KIRI_ON, timeout=1)
|
||||||
|
threading.Timer(DETIK_ANTAR_KARUNG, relay_kiri_off).start()
|
||||||
|
print(f"Relay Kiri ON -> Start Counter Kiri: {DETIK_ANTAR_KARUNG} seconds")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
# =====================
|
# =====================
|
||||||
# RESET HARIAN
|
# RESET HARIAN
|
||||||
# =====================
|
# =====================
|
||||||
def reset_counter():
|
def reset_counter():
|
||||||
global counter, seen_objects
|
global counter, seen_objects
|
||||||
print(f"[{datetime.now()}] Reset counter otomatis")
|
print(f"Reset counter otomatis")
|
||||||
# Save current counter values to database before reset
|
# Save current counter values to database before reset
|
||||||
save_counter_to_database(camera_feeder)
|
save_counter_to_database(camera_feeder)
|
||||||
# reset semua
|
# reset semua
|
||||||
@@ -166,6 +193,7 @@ def reset_counter():
|
|||||||
republish_counter()
|
republish_counter()
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
def reset_counter_masuk():
|
def reset_counter_masuk():
|
||||||
global counter, seen_objects
|
global counter, seen_objects
|
||||||
print(f"[{datetime.now()}] Reset counter otomatis")
|
print(f"[{datetime.now()}] Reset counter otomatis")
|
||||||
@@ -178,12 +206,13 @@ def reset_counter_masuk():
|
|||||||
for camera_name in camera_masuk:
|
for camera_name in camera_masuk:
|
||||||
counter[camera_name]["karung"] = 0
|
counter[camera_name]["karung"] = 0
|
||||||
|
|
||||||
counter["kandang_1_karung_masuk"]["karung"] = 0
|
#counter["kandang_1_karung_masuk"]["karung"] = 0
|
||||||
|
|
||||||
seen_objects = {}
|
seen_objects = {}
|
||||||
save_counter()
|
save_counter()
|
||||||
schedule_reset_masuk() # jadwalkan besok
|
schedule_reset_masuk() # jadwalkan besok
|
||||||
republish_counter()
|
republish_counter()
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def schedule_reset():
|
def schedule_reset():
|
||||||
@@ -196,6 +225,7 @@ def schedule_reset():
|
|||||||
print(f"Reset counter dijadwalkan pada: {reset_time}")
|
print(f"Reset counter dijadwalkan pada: {reset_time}")
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
def schedule_reset_masuk():
|
def schedule_reset_masuk():
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
reset_time = now.replace(hour=23, minute=59, second=55, microsecond=0)
|
reset_time = now.replace(hour=23, minute=59, second=55, microsecond=0)
|
||||||
@@ -204,6 +234,7 @@ def schedule_reset_masuk():
|
|||||||
delay = (reset_time - now).total_seconds()
|
delay = (reset_time - now).total_seconds()
|
||||||
threading.Timer(delay, reset_counter_masuk).start()
|
threading.Timer(delay, reset_counter_masuk).start()
|
||||||
print(f"Reset counter dijadwalkan pada: {reset_time}")
|
print(f"Reset counter dijadwalkan pada: {reset_time}")
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
# =====================
|
# =====================
|
||||||
@@ -221,6 +252,7 @@ def on_connect_publish(client, userdata, flags, rc):
|
|||||||
|
|
||||||
|
|
||||||
def on_message(client, userdata, msg):
|
def on_message(client, userdata, msg):
|
||||||
|
global DETIK_ANTAR_KARUNG
|
||||||
try:
|
try:
|
||||||
payload = json.loads(msg.payload.decode())
|
payload = json.loads(msg.payload.decode())
|
||||||
if "after" not in payload:
|
if "after" not in payload:
|
||||||
@@ -271,7 +303,9 @@ def on_message(client, userdata, msg):
|
|||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
if camera in camera_feeder:
|
if camera in camera_feeder:
|
||||||
DETIK_ANTAR_KARUNG = 35
|
#DETIK_ANTAR_KARUNG = 35
|
||||||
|
# Update 20260411 to 30s
|
||||||
|
#DETIK_ANTAR_KARUNG = 30
|
||||||
current_time = datetime.now()
|
current_time = datetime.now()
|
||||||
for ts in seen_objects[camera].values():
|
for ts in seen_objects[camera].values():
|
||||||
delta = current_time - ts
|
delta = current_time - ts
|
||||||
@@ -283,7 +317,7 @@ def on_message(client, userdata, msg):
|
|||||||
seen_objects[camera][track_id] = datetime.now()
|
seen_objects[camera][track_id] = datetime.now()
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"[{datetime.now()}] Kamera {camera}: Object {label} masuk zona {new_zones}"
|
f"Kamera {camera}: Object {label} masuk zona {new_zones}"
|
||||||
)
|
)
|
||||||
print(f"Total {label} di {camera}: {counter[camera][label]}")
|
print(f"Total {label} di {camera}: {counter[camera][label]}")
|
||||||
|
|
||||||
@@ -297,6 +331,10 @@ def on_message(client, userdata, msg):
|
|||||||
f"{SITE_TOPIC}/{camera}/{label}", counter[camera][label], qos=1, retain=True
|
f"{SITE_TOPIC}/{camera}/{label}", counter[camera][label], qos=1, retain=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Start Relay
|
||||||
|
if camera == "kandang_atas_feeder_kiri":
|
||||||
|
relay_kiri_on()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error parsing message:", e)
|
print("Error parsing message:", e)
|
||||||
|
|
||||||
@@ -333,6 +371,8 @@ client_publish.connect(PUBLISH_BROKER, PUBLISH_PORT, 60)
|
|||||||
# jadwalkan reset pertama
|
# jadwalkan reset pertama
|
||||||
schedule_reset()
|
schedule_reset()
|
||||||
#schedule_reset_masuk()
|
#schedule_reset_masuk()
|
||||||
|
# Pastikan Relay OFF
|
||||||
|
relay_kiri_off()
|
||||||
|
|
||||||
client_publish.loop_start()
|
client_publish.loop_start()
|
||||||
print(f"MQTT Publish Counter berjalan di {PUBLISH_BROKER}")
|
print(f"MQTT Publish Counter berjalan di {PUBLISH_BROKER}")
|
||||||
|
|||||||
342
frigate_counter.py.backup
Normal file
342
frigate_counter.py.backup
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import threading
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# SITE CONFIG
|
||||||
|
# =====================
|
||||||
|
TOP_TOPIC = "cpsp"
|
||||||
|
SITE_NAME = "sukawarna"
|
||||||
|
SITE_TOPIC = f"{TOP_TOPIC}/counter/{SITE_NAME}"
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# KONFIGURASI MQTT
|
||||||
|
# =====================
|
||||||
|
BROKER = "localhost" # ganti sesuai broker
|
||||||
|
PORT = 1883
|
||||||
|
USERNAME = "" # ganti user
|
||||||
|
PASSWORD = "" # ganti password
|
||||||
|
TOPIC = "frigate/events"
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# MQTT PUBLISH
|
||||||
|
# =====================
|
||||||
|
#PUBLISH_BROKER = "localhost"
|
||||||
|
PUBLISH_BROKER = "mqtt.backone.cloud"
|
||||||
|
PUBLISH_PORT = 1883
|
||||||
|
USERNAME = "" # ganti user
|
||||||
|
PASSWORD = "" # ganti password
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# FILE COUNTER
|
||||||
|
# =====================
|
||||||
|
DATA_FILE = "/etc/frigate-counter/karung-tuang/karung_tuang.json"
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# DATABASE INITIALIZATION
|
||||||
|
# =====================
|
||||||
|
DB_FILE = "/etc/frigate-counter/karung-tuang/karung_tuang.db"
|
||||||
|
|
||||||
|
|
||||||
|
def init_database():
|
||||||
|
"""Initialize the SQLite database with the required table"""
|
||||||
|
conn = sqlite3.connect(DB_FILE)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Create table if it doesn't exist
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS counter_data (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
camera_name TEXT NOT NULL,
|
||||||
|
date TIMESTAMP NOT NULL,
|
||||||
|
counter_value INTEGER NOT NULL
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
# Initialize the database
|
||||||
|
init_database()
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# INISIALISASI COUNTER
|
||||||
|
# =====================
|
||||||
|
if os.path.exists(DATA_FILE):
|
||||||
|
with open(DATA_FILE, "r") as f:
|
||||||
|
counter = json.load(f)
|
||||||
|
else:
|
||||||
|
counter = {
|
||||||
|
"kandang_atas_feeder_kiri": {"karung": 0},
|
||||||
|
"kandang_bawah_feeder_kanan": {"karung": 0},
|
||||||
|
}
|
||||||
|
with open(DATA_FILE, "w") as f:
|
||||||
|
json.dump(counter, f, indent=2)
|
||||||
|
|
||||||
|
camera_feeder_no_duplicate = [
|
||||||
|
"kandang_atas_feeder_kiri",
|
||||||
|
"kandang_bawah_feeder_kanan"
|
||||||
|
]
|
||||||
|
|
||||||
|
camera_feeder = [
|
||||||
|
"kandang_atas_feeder_kiri",
|
||||||
|
"kandang_bawah_feeder_kanan"
|
||||||
|
]
|
||||||
|
|
||||||
|
camera_masuk = ["kandang_1_karung_masuk"]
|
||||||
|
|
||||||
|
# Untuk debounce object per track_id
|
||||||
|
seen_objects = {} # dict per kamera: {track_id: last_seen}
|
||||||
|
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# FUNGSI SAVE COUNTER
|
||||||
|
# =====================
|
||||||
|
def save_counter():
|
||||||
|
with open(DATA_FILE, "w") as f:
|
||||||
|
json.dump(counter, f, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
def save_counter_to_database(camera_list_to_save=[]):
|
||||||
|
"""Save current counter values to SQLite database"""
|
||||||
|
conn = sqlite3.connect(DB_FILE)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Get current timestamp
|
||||||
|
current_time = datetime.now()
|
||||||
|
|
||||||
|
# Save each counter value to database
|
||||||
|
for camera_name, camera_data in counter.items():
|
||||||
|
for label, value in camera_data.items():
|
||||||
|
if label == "karung" and camera_name in camera_list_to_save: # Only save karung counters
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO counter_data (camera_name, date, counter_value)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
""",
|
||||||
|
(camera_name, current_time, value),
|
||||||
|
)
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def republish_counter():
|
||||||
|
# Publish current counter
|
||||||
|
for camera, v in counter.items():
|
||||||
|
for label, value in v.items():
|
||||||
|
if label == "karung":
|
||||||
|
print(f"{SITE_TOPIC}/{camera}/{label}", value)
|
||||||
|
client_publish.publish(
|
||||||
|
f"{SITE_TOPIC}/{camera}/{label}", value, qos=1, retain=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# RESET HARIAN
|
||||||
|
# =====================
|
||||||
|
def reset_counter():
|
||||||
|
global counter, seen_objects
|
||||||
|
print(f"[{datetime.now()}] Reset counter otomatis")
|
||||||
|
# Save current counter values to database before reset
|
||||||
|
save_counter_to_database(camera_feeder)
|
||||||
|
# reset semua
|
||||||
|
# for cam in counter:
|
||||||
|
# for obj in counter[cam]:
|
||||||
|
# counter[cam][obj] = 0
|
||||||
|
for camera_name in camera_feeder:
|
||||||
|
counter[camera_name]["karung"] = 0
|
||||||
|
|
||||||
|
"""
|
||||||
|
counter["kandang_1_karung_pakan"]["karung"] = 0
|
||||||
|
counter["kandang_2_karung_pakan"]["karung"] = 0
|
||||||
|
counter["kandang_atas_feeder_kiri"]["karung"] = 0
|
||||||
|
counter["kandang_bawah_feeder_kanan"]["karung"] = 0
|
||||||
|
"""
|
||||||
|
seen_objects = {}
|
||||||
|
save_counter()
|
||||||
|
schedule_reset() # jadwalkan besok
|
||||||
|
republish_counter()
|
||||||
|
|
||||||
|
|
||||||
|
def reset_counter_masuk():
|
||||||
|
global counter, seen_objects
|
||||||
|
print(f"[{datetime.now()}] Reset counter otomatis")
|
||||||
|
# Save current counter values to database before reset
|
||||||
|
save_counter_to_database(camera_masuk)
|
||||||
|
# reset semua
|
||||||
|
# for cam in counter:
|
||||||
|
# for obj in counter[cam]:
|
||||||
|
# counter[cam][obj] = 0
|
||||||
|
for camera_name in camera_masuk:
|
||||||
|
counter[camera_name]["karung"] = 0
|
||||||
|
|
||||||
|
counter["kandang_1_karung_masuk"]["karung"] = 0
|
||||||
|
|
||||||
|
seen_objects = {}
|
||||||
|
save_counter()
|
||||||
|
schedule_reset_masuk() # jadwalkan besok
|
||||||
|
republish_counter()
|
||||||
|
|
||||||
|
|
||||||
|
def schedule_reset():
|
||||||
|
now = datetime.now()
|
||||||
|
reset_time = now.replace(hour=17, minute=0, second=0, microsecond=0)
|
||||||
|
if now >= reset_time:
|
||||||
|
reset_time += timedelta(days=1)
|
||||||
|
delay = (reset_time - now).total_seconds()
|
||||||
|
threading.Timer(delay, reset_counter).start()
|
||||||
|
print(f"Reset counter dijadwalkan pada: {reset_time}")
|
||||||
|
|
||||||
|
|
||||||
|
def schedule_reset_masuk():
|
||||||
|
now = datetime.now()
|
||||||
|
reset_time = now.replace(hour=23, minute=59, second=55, microsecond=0)
|
||||||
|
if now >= reset_time:
|
||||||
|
reset_time += timedelta(days=1)
|
||||||
|
delay = (reset_time - now).total_seconds()
|
||||||
|
threading.Timer(delay, reset_counter_masuk).start()
|
||||||
|
print(f"Reset counter dijadwalkan pada: {reset_time}")
|
||||||
|
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# CALLBACK MQTT
|
||||||
|
# =====================
|
||||||
|
def on_connect(client, userdata, flags, rc):
|
||||||
|
print("Connected to MQTT with result code " + str(rc))
|
||||||
|
client.subscribe(TOPIC)
|
||||||
|
|
||||||
|
|
||||||
|
def on_connect_publish(client, userdata, flags, rc):
|
||||||
|
print("Connected to MQTT PUBLISH with result code " + str(rc))
|
||||||
|
# Publish current counter
|
||||||
|
republish_counter()
|
||||||
|
|
||||||
|
|
||||||
|
def on_message(client, userdata, msg):
|
||||||
|
try:
|
||||||
|
payload = json.loads(msg.payload.decode())
|
||||||
|
if "after" not in payload:
|
||||||
|
return
|
||||||
|
|
||||||
|
event_after = payload["after"]
|
||||||
|
event_before = payload.get("before", {})
|
||||||
|
|
||||||
|
camera = event_after.get("camera")
|
||||||
|
label = event_after.get("label")
|
||||||
|
track_id = event_after.get("id")
|
||||||
|
zones_after = event_after.get("entered_zones", [])
|
||||||
|
zones_before = event_before.get("entered_zones", [])
|
||||||
|
|
||||||
|
# Dont detect stationary
|
||||||
|
stationary = event_after.get("stationary")
|
||||||
|
if stationary:
|
||||||
|
return
|
||||||
|
|
||||||
|
# ambil zona baru yg sebelumnya belum ada
|
||||||
|
new_zones = [z for z in zones_after if z not in zones_before]
|
||||||
|
|
||||||
|
# DEDY Don't count camera_masuk. Only count camera feeder
|
||||||
|
#if camera not in camera_feeder:
|
||||||
|
# return
|
||||||
|
|
||||||
|
if not camera or not label:
|
||||||
|
return
|
||||||
|
if camera not in counter or label not in counter[camera]:
|
||||||
|
return
|
||||||
|
if not new_zones: # hanya hitung kalau masuk zona baru
|
||||||
|
return
|
||||||
|
|
||||||
|
# debounce per track_id
|
||||||
|
if camera not in seen_objects:
|
||||||
|
seen_objects[camera] = {}
|
||||||
|
if track_id in seen_objects[camera]:
|
||||||
|
return # sudah dihitung
|
||||||
|
|
||||||
|
# Check if timestamp is more than 30 seconds
|
||||||
|
# if camera == "kandang_1_karung_pakan" or camera == "kandang_2_karung_pakan":
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
camera == "kandang_1_karung_pakan"
|
||||||
|
or camera == "kandang_2_karung_pakan"
|
||||||
|
or camera == "kandang_atas_feeder_kiri"
|
||||||
|
or camera == "kandang_bawah_feeder_kanan"
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
if camera in camera_feeder:
|
||||||
|
#DETIK_ANTAR_KARUNG = 35
|
||||||
|
# Update 20260411 to 30s
|
||||||
|
DETIK_ANTAR_KARUNG = 30
|
||||||
|
current_time = datetime.now()
|
||||||
|
for ts in seen_objects[camera].values():
|
||||||
|
delta = current_time - ts
|
||||||
|
if delta.seconds < DETIK_ANTAR_KARUNG:
|
||||||
|
return
|
||||||
|
|
||||||
|
# hitung
|
||||||
|
counter[camera][label] += 1
|
||||||
|
seen_objects[camera][track_id] = datetime.now()
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"[{datetime.now()}] Kamera {camera}: Object {label} masuk zona {new_zones}"
|
||||||
|
)
|
||||||
|
print(f"Total {label} di {camera}: {counter[camera][label]}")
|
||||||
|
|
||||||
|
print(f"{TOP_TOPIC}/counter/{SITE_NAME}/{camera}/{label}")
|
||||||
|
|
||||||
|
# simpan dan publish
|
||||||
|
save_counter()
|
||||||
|
#save_counter_to_database(camera_feeder)
|
||||||
|
# client_publish.publish(f"{TOP_TOPIC}/counter/{SITE_NAME}/{camera}/{label}", counter[camera][label], qos=1, retain=True)
|
||||||
|
client_publish.publish(
|
||||||
|
f"{SITE_TOPIC}/{camera}/{label}", counter[camera][label], qos=1, retain=True
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("Error parsing message:", e)
|
||||||
|
|
||||||
|
|
||||||
|
def view_database():
|
||||||
|
"""View all records in the database"""
|
||||||
|
conn = sqlite3.connect(DB_FILE)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
cursor.execute("SELECT * FROM counter_data ORDER BY date DESC LIMIT 10")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
|
print("Last 10 records in database:")
|
||||||
|
for row in rows:
|
||||||
|
print(f"ID: {row[0]}, Camera: {row[1]}, Date: {row[2]}, Counter: {row[3]}")
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# MAIN
|
||||||
|
# =====================
|
||||||
|
client = mqtt.Client()
|
||||||
|
client.username_pw_set(USERNAME, PASSWORD)
|
||||||
|
client.on_connect = on_connect
|
||||||
|
client.on_message = on_message
|
||||||
|
|
||||||
|
client.connect(BROKER, PORT, 60)
|
||||||
|
|
||||||
|
client_publish = mqtt.Client()
|
||||||
|
client_publish.on_connect = on_connect_publish
|
||||||
|
client_publish.connect(PUBLISH_BROKER, PUBLISH_PORT, 60)
|
||||||
|
|
||||||
|
|
||||||
|
# jadwalkan reset pertama
|
||||||
|
schedule_reset()
|
||||||
|
#schedule_reset_masuk()
|
||||||
|
|
||||||
|
client_publish.loop_start()
|
||||||
|
print(f"MQTT Publish Counter berjalan di {PUBLISH_BROKER}")
|
||||||
|
print(f"MQTT Counter berjalan di {BROKER}")
|
||||||
|
client.loop_forever()
|
||||||
380
frigate_counter.py.new
Normal file
380
frigate_counter.py.new
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import threading
|
||||||
|
import requests
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# SITE CONFIG
|
||||||
|
# =====================
|
||||||
|
TOP_TOPIC = "cpsp"
|
||||||
|
SITE_NAME = "sukawarna"
|
||||||
|
SITE_TOPIC = f"{TOP_TOPIC}/counter/{SITE_NAME}"
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# KONFIGURASI MQTT
|
||||||
|
# =====================
|
||||||
|
BROKER = "localhost" # ganti sesuai broker
|
||||||
|
PORT = 1883
|
||||||
|
USERNAME = "" # ganti user
|
||||||
|
PASSWORD = "" # ganti password
|
||||||
|
TOPIC = "frigate/events"
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# MQTT PUBLISH
|
||||||
|
# =====================
|
||||||
|
#PUBLISH_BROKER = "localhost"
|
||||||
|
PUBLISH_BROKER = "mqtt.backone.cloud"
|
||||||
|
PUBLISH_PORT = 1883
|
||||||
|
USERNAME = "" # ganti user
|
||||||
|
PASSWORD = "" # ganti password
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# FILE COUNTER
|
||||||
|
# =====================
|
||||||
|
DATA_FILE = "/etc/frigate-counter/karung-tuang/karung_tuang.json"
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# DATABASE INITIALIZATION
|
||||||
|
# =====================
|
||||||
|
DB_FILE = "/etc/frigate-counter/karung-tuang/karung_tuang.db"
|
||||||
|
|
||||||
|
DETIK_ANTAR_KARUNG = 30
|
||||||
|
|
||||||
|
RELAY_KIRI_ON = "http://192.168.192.26:5001/relay_on"
|
||||||
|
RELAY_KIRI_OFF = "http://192.168.192.26:5001/relay_off"
|
||||||
|
|
||||||
|
|
||||||
|
def init_database():
|
||||||
|
"""Initialize the SQLite database with the required table"""
|
||||||
|
conn = sqlite3.connect(DB_FILE)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Create table if it doesn't exist
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS counter_data (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
camera_name TEXT NOT NULL,
|
||||||
|
date TIMESTAMP NOT NULL,
|
||||||
|
counter_value INTEGER NOT NULL
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
# Initialize the database
|
||||||
|
init_database()
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# INISIALISASI COUNTER
|
||||||
|
# =====================
|
||||||
|
if os.path.exists(DATA_FILE):
|
||||||
|
with open(DATA_FILE, "r") as f:
|
||||||
|
counter = json.load(f)
|
||||||
|
else:
|
||||||
|
counter = {
|
||||||
|
"kandang_atas_feeder_kiri": {"karung": 0},
|
||||||
|
"kandang_bawah_feeder_kanan": {"karung": 0},
|
||||||
|
}
|
||||||
|
with open(DATA_FILE, "w") as f:
|
||||||
|
json.dump(counter, f, indent=2)
|
||||||
|
|
||||||
|
camera_feeder_no_duplicate = [
|
||||||
|
"kandang_atas_feeder_kiri",
|
||||||
|
"kandang_bawah_feeder_kanan"
|
||||||
|
]
|
||||||
|
|
||||||
|
camera_feeder = [
|
||||||
|
"kandang_atas_feeder_kiri",
|
||||||
|
"kandang_bawah_feeder_kanan"
|
||||||
|
]
|
||||||
|
|
||||||
|
camera_masuk = ["kandang_1_karung_masuk"]
|
||||||
|
|
||||||
|
# Untuk debounce object per track_id
|
||||||
|
seen_objects = {} # dict per kamera: {track_id: last_seen}
|
||||||
|
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# FUNGSI SAVE COUNTER
|
||||||
|
# =====================
|
||||||
|
def save_counter():
|
||||||
|
with open(DATA_FILE, "w") as f:
|
||||||
|
json.dump(counter, f, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
def save_counter_to_database(camera_list_to_save=[]):
|
||||||
|
"""Save current counter values to SQLite database"""
|
||||||
|
conn = sqlite3.connect(DB_FILE)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Get current timestamp
|
||||||
|
current_time = datetime.now()
|
||||||
|
|
||||||
|
# Save each counter value to database
|
||||||
|
for camera_name, camera_data in counter.items():
|
||||||
|
for label, value in camera_data.items():
|
||||||
|
if label == "karung" and camera_name in camera_list_to_save: # Only save karung counters
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO counter_data (camera_name, date, counter_value)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
""",
|
||||||
|
(camera_name, current_time, value),
|
||||||
|
)
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def republish_counter():
|
||||||
|
# Publish current counter
|
||||||
|
for camera, v in counter.items():
|
||||||
|
for label, value in v.items():
|
||||||
|
if label == "karung":
|
||||||
|
print(f"{SITE_TOPIC}/{camera}/{label}", value)
|
||||||
|
client_publish.publish(
|
||||||
|
f"{SITE_TOPIC}/{camera}/{label}", value, qos=1, retain=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# RELAY FUNCTION
|
||||||
|
# =====================
|
||||||
|
def relay_kiri_off():
|
||||||
|
global RELAY_KIRI_ON, RELAY_KIRI_OFF
|
||||||
|
try:
|
||||||
|
requests.get(RELAY_KIRI_OFF, timeout=1)
|
||||||
|
print(f"Relay Kiri OFF")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
def relay_kiri_on():
|
||||||
|
global DETIK_ANTAR_KARUNG
|
||||||
|
|
||||||
|
try:
|
||||||
|
requests.get(RELAY_KIRI_ON, timeout=1)
|
||||||
|
threading.Timer(DETIK_ANTAR_KARUNG, relay_kiri_off).start()
|
||||||
|
print(f"Relay Kiri ON -> Start Counter Kiri: {DETIK_ANTAR_KARUNG} seconds")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# RESET HARIAN
|
||||||
|
# =====================
|
||||||
|
def reset_counter():
|
||||||
|
global counter, seen_objects
|
||||||
|
print(f"Reset counter otomatis")
|
||||||
|
# Save current counter values to database before reset
|
||||||
|
save_counter_to_database(camera_feeder)
|
||||||
|
# reset semua
|
||||||
|
# for cam in counter:
|
||||||
|
# for obj in counter[cam]:
|
||||||
|
# counter[cam][obj] = 0
|
||||||
|
for camera_name in camera_feeder:
|
||||||
|
counter[camera_name]["karung"] = 0
|
||||||
|
|
||||||
|
"""
|
||||||
|
counter["kandang_1_karung_pakan"]["karung"] = 0
|
||||||
|
counter["kandang_2_karung_pakan"]["karung"] = 0
|
||||||
|
counter["kandang_atas_feeder_kiri"]["karung"] = 0
|
||||||
|
counter["kandang_bawah_feeder_kanan"]["karung"] = 0
|
||||||
|
"""
|
||||||
|
seen_objects = {}
|
||||||
|
save_counter()
|
||||||
|
schedule_reset() # jadwalkan besok
|
||||||
|
republish_counter()
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
def reset_counter_masuk():
|
||||||
|
global counter, seen_objects
|
||||||
|
print(f"[{datetime.now()}] Reset counter otomatis")
|
||||||
|
# Save current counter values to database before reset
|
||||||
|
save_counter_to_database(camera_masuk)
|
||||||
|
# reset semua
|
||||||
|
# for cam in counter:
|
||||||
|
# for obj in counter[cam]:
|
||||||
|
# counter[cam][obj] = 0
|
||||||
|
for camera_name in camera_masuk:
|
||||||
|
counter[camera_name]["karung"] = 0
|
||||||
|
|
||||||
|
#counter["kandang_1_karung_masuk"]["karung"] = 0
|
||||||
|
|
||||||
|
seen_objects = {}
|
||||||
|
save_counter()
|
||||||
|
schedule_reset_masuk() # jadwalkan besok
|
||||||
|
republish_counter()
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def schedule_reset():
|
||||||
|
now = datetime.now()
|
||||||
|
reset_time = now.replace(hour=17, minute=0, second=0, microsecond=0)
|
||||||
|
if now >= reset_time:
|
||||||
|
reset_time += timedelta(days=1)
|
||||||
|
delay = (reset_time - now).total_seconds()
|
||||||
|
threading.Timer(delay, reset_counter).start()
|
||||||
|
print(f"Reset counter dijadwalkan pada: {reset_time}")
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
def schedule_reset_masuk():
|
||||||
|
now = datetime.now()
|
||||||
|
reset_time = now.replace(hour=23, minute=59, second=55, microsecond=0)
|
||||||
|
if now >= reset_time:
|
||||||
|
reset_time += timedelta(days=1)
|
||||||
|
delay = (reset_time - now).total_seconds()
|
||||||
|
threading.Timer(delay, reset_counter_masuk).start()
|
||||||
|
print(f"Reset counter dijadwalkan pada: {reset_time}")
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# CALLBACK MQTT
|
||||||
|
# =====================
|
||||||
|
def on_connect(client, userdata, flags, rc):
|
||||||
|
print("Connected to MQTT with result code " + str(rc))
|
||||||
|
client.subscribe(TOPIC)
|
||||||
|
|
||||||
|
|
||||||
|
def on_connect_publish(client, userdata, flags, rc):
|
||||||
|
print("Connected to MQTT PUBLISH with result code " + str(rc))
|
||||||
|
# Publish current counter
|
||||||
|
republish_counter()
|
||||||
|
|
||||||
|
|
||||||
|
def on_message(client, userdata, msg):
|
||||||
|
global DETIK_ANTAR_KARUNG
|
||||||
|
try:
|
||||||
|
payload = json.loads(msg.payload.decode())
|
||||||
|
if "after" not in payload:
|
||||||
|
return
|
||||||
|
|
||||||
|
event_after = payload["after"]
|
||||||
|
event_before = payload.get("before", {})
|
||||||
|
|
||||||
|
camera = event_after.get("camera")
|
||||||
|
label = event_after.get("label")
|
||||||
|
track_id = event_after.get("id")
|
||||||
|
zones_after = event_after.get("entered_zones", [])
|
||||||
|
zones_before = event_before.get("entered_zones", [])
|
||||||
|
|
||||||
|
# Dont detect stationary
|
||||||
|
stationary = event_after.get("stationary")
|
||||||
|
if stationary:
|
||||||
|
return
|
||||||
|
|
||||||
|
# ambil zona baru yg sebelumnya belum ada
|
||||||
|
new_zones = [z for z in zones_after if z not in zones_before]
|
||||||
|
|
||||||
|
# DEDY Don't count camera_masuk. Only count camera feeder
|
||||||
|
#if camera not in camera_feeder:
|
||||||
|
# return
|
||||||
|
|
||||||
|
if not camera or not label:
|
||||||
|
return
|
||||||
|
if camera not in counter or label not in counter[camera]:
|
||||||
|
return
|
||||||
|
if not new_zones: # hanya hitung kalau masuk zona baru
|
||||||
|
return
|
||||||
|
|
||||||
|
# debounce per track_id
|
||||||
|
if camera not in seen_objects:
|
||||||
|
seen_objects[camera] = {}
|
||||||
|
if track_id in seen_objects[camera]:
|
||||||
|
return # sudah dihitung
|
||||||
|
|
||||||
|
# Check if timestamp is more than 30 seconds
|
||||||
|
# if camera == "kandang_1_karung_pakan" or camera == "kandang_2_karung_pakan":
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
camera == "kandang_1_karung_pakan"
|
||||||
|
or camera == "kandang_2_karung_pakan"
|
||||||
|
or camera == "kandang_atas_feeder_kiri"
|
||||||
|
or camera == "kandang_bawah_feeder_kanan"
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
if camera in camera_feeder:
|
||||||
|
#DETIK_ANTAR_KARUNG = 35
|
||||||
|
# Update 20260411 to 30s
|
||||||
|
#DETIK_ANTAR_KARUNG = 30
|
||||||
|
current_time = datetime.now()
|
||||||
|
for ts in seen_objects[camera].values():
|
||||||
|
delta = current_time - ts
|
||||||
|
if delta.seconds < DETIK_ANTAR_KARUNG:
|
||||||
|
return
|
||||||
|
|
||||||
|
# hitung
|
||||||
|
counter[camera][label] += 1
|
||||||
|
seen_objects[camera][track_id] = datetime.now()
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Kamera {camera}: Object {label} masuk zona {new_zones}"
|
||||||
|
)
|
||||||
|
print(f"Total {label} di {camera}: {counter[camera][label]}")
|
||||||
|
|
||||||
|
print(f"{TOP_TOPIC}/counter/{SITE_NAME}/{camera}/{label}")
|
||||||
|
|
||||||
|
# simpan dan publish
|
||||||
|
save_counter()
|
||||||
|
#save_counter_to_database(camera_feeder)
|
||||||
|
# client_publish.publish(f"{TOP_TOPIC}/counter/{SITE_NAME}/{camera}/{label}", counter[camera][label], qos=1, retain=True)
|
||||||
|
client_publish.publish(
|
||||||
|
f"{SITE_TOPIC}/{camera}/{label}", counter[camera][label], qos=1, retain=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Start Relay
|
||||||
|
if camera == "kandang_atas_feeder_kiri":
|
||||||
|
relay_kiri_on()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("Error parsing message:", e)
|
||||||
|
|
||||||
|
|
||||||
|
def view_database():
|
||||||
|
"""View all records in the database"""
|
||||||
|
conn = sqlite3.connect(DB_FILE)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
cursor.execute("SELECT * FROM counter_data ORDER BY date DESC LIMIT 10")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
|
print("Last 10 records in database:")
|
||||||
|
for row in rows:
|
||||||
|
print(f"ID: {row[0]}, Camera: {row[1]}, Date: {row[2]}, Counter: {row[3]}")
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# MAIN
|
||||||
|
# =====================
|
||||||
|
client = mqtt.Client()
|
||||||
|
client.username_pw_set(USERNAME, PASSWORD)
|
||||||
|
client.on_connect = on_connect
|
||||||
|
client.on_message = on_message
|
||||||
|
|
||||||
|
client.connect(BROKER, PORT, 60)
|
||||||
|
|
||||||
|
client_publish = mqtt.Client()
|
||||||
|
client_publish.on_connect = on_connect_publish
|
||||||
|
client_publish.connect(PUBLISH_BROKER, PUBLISH_PORT, 60)
|
||||||
|
|
||||||
|
|
||||||
|
# jadwalkan reset pertama
|
||||||
|
schedule_reset()
|
||||||
|
#schedule_reset_masuk()
|
||||||
|
# Pastikan Relay OFF
|
||||||
|
relay_kiri_off()
|
||||||
|
|
||||||
|
client_publish.loop_start()
|
||||||
|
print(f"MQTT Publish Counter berjalan di {PUBLISH_BROKER}")
|
||||||
|
print(f"MQTT Counter berjalan di {BROKER}")
|
||||||
|
client.loop_forever()
|
||||||
BIN
karung_tuang.db
BIN
karung_tuang.db
Binary file not shown.
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"kandang_atas_feeder_kiri": {
|
"kandang_atas_feeder_kiri": {
|
||||||
"karung": 12
|
"karung": 0
|
||||||
},
|
},
|
||||||
"kandang_bawah_feeder_kanan": {
|
"kandang_bawah_feeder_kanan": {
|
||||||
"karung": 16
|
"karung": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user