Disabled Motion Boost

This commit is contained in:
2026-02-01 14:49:27 +05:30
parent 98e8eca000
commit c40367e7b3
2 changed files with 33348 additions and 51 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ import time
import requests
# ---------- Camera ----------
CAMERA = os.environ.get("CAMERA", "/dev/video0")
CAMERA = "/dev/video0"
TMP_IMG = "/tmp/desk_current.jpg"
PREV_IMG = "/tmp/desk_prev.jpg"
@@ -18,22 +18,25 @@ HA_STATE_FILE = "/tmp/desk_presence_last_ha.json"
LIGHT_COOLDOWN_FILE = "/tmp/desk_light_cooldown.json"
# ---------- Haar cascade ----------
FACE_CASCADE = cv2.CascadeClassifier(
"/usr/share/opencv4/haarcascades/haarcascade_frontalface_default.xml"
)
FACE_CASCADE = cv2.CascadeClassifier("cascades/haarcascade_frontalface_default.xml")
if FACE_CASCADE.empty():
raise RuntimeError("Failed to load Haar cascade")
# ---------- Home Assistant ----------
HA_URL = os.environ.get("HA_URL", "http://192.168.0.202:8123")
HA_ENTITY_ID = os.environ.get("HA_ENTITY_ID", "binary_sensor.desk_presence_vision")
HA_TOKEN = os.environ.get("HA_TOKEN")
if not HA_TOKEN:
raise RuntimeError("HA_TOKEN environment variable not set")
# ---------- Presence logic ----------
MAX_SCORE = 5
FACE_BOOST = 2
MOTION_BOOST = 1
MOTION_BOOST = 0 # motion never increases score
DECAY = 1
MOTION_AREA_THRESHOLD = 4000
MOTION_AREA_THRESHOLD = 8000
LIGHT_COOLDOWN_SECONDS = 15
# Adaptive delays (seconds)
@@ -53,9 +56,8 @@ def safe_delete(path):
try:
if os.path.exists(path):
os.remove(path)
print(f"[CLEANUP] Deleted {path}")
except Exception as e:
print(f"[WARN] Could not delete {path}: {e}")
except Exception:
pass
def capture():
@@ -114,11 +116,7 @@ def record_light_off_event():
def ignore_motion_due_to_light():
data = load_json(LIGHT_COOLDOWN_FILE, {})
ts = data.get("ts", 0)
remaining = LIGHT_COOLDOWN_SECONDS - (time.time() - ts)
if remaining > 0:
print(f"[INFO] Ignoring motion for {remaining:.1f}s (light cooldown)")
return True
return False
return (time.time() - ts) < LIGHT_COOLDOWN_SECONDS
def send_to_ha(present):
@@ -126,7 +124,6 @@ def send_to_ha(present):
last_state = load_last_ha_state()
if new_state == last_state:
print("[HA] No state change")
return
headers = {
@@ -142,25 +139,17 @@ def send_to_ha(present):
},
}
try:
r = requests.post(
f"{HA_URL}/api/states/{HA_ENTITY_ID}",
headers=headers,
json=payload,
timeout=5,
)
r = requests.post(
f"{HA_URL}/api/states/{HA_ENTITY_ID}",
headers=headers,
json=payload,
timeout=5,
)
if r.status_code in (200, 201):
print(f"[HA] Updated state → {new_state}")
save_last_ha_state(new_state)
if new_state == "off":
record_light_off_event()
else:
print(f"[HA] Error {r.status_code}: {r.text}")
except Exception as e:
print(f"[HA] Connection failed: {e}")
if r.status_code in (200, 201):
save_last_ha_state(new_state)
if new_state == "off":
record_light_off_event()
# ---------- Detection ----------
@@ -183,15 +172,20 @@ def detect_motion(gray):
cv2.imwrite(PREV_IMG, gray)
diff = cv2.absdiff(prev, gray)
_, thresh = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)
# Ignore global brightness / exposure shifts
mean_diff = np.mean(diff)
if mean_diff > 12:
return False
_, thresh = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)
kernel = np.ones((5, 5), np.uint8)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
area = cv2.countNonZero(thresh)
print("[DEBUG] Motion area:", area)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return area > MOTION_AREA_THRESHOLD
max_area = max((cv2.contourArea(c) for c in contours), default=0)
return max_area > MOTION_AREA_THRESHOLD
def get_delay(score):
@@ -200,7 +194,7 @@ def get_delay(score):
# ---------- Main loop ----------
def main_loop():
print("=== SNAPSHOT DESK PRESENCE (STABLE) ===")
print("=== SNAPSHOT DESK PRESENCE ===")
while True:
try:
@@ -208,7 +202,6 @@ def main_loop():
frame = cv2.imread(TMP_IMG)
if frame is None:
print("[ERROR] Frame read failed")
time.sleep(30)
continue
@@ -218,33 +211,23 @@ def main_loop():
motion = detect_motion(gray)
score = load_score()
print("\nPrevious score:", score)
if face:
score += FACE_BOOST
print("Face detected → +", FACE_BOOST)
elif motion:
score += MOTION_BOOST
print("Motion detected → +", MOTION_BOOST)
else:
score -= DECAY
print("No signal → -", DECAY)
score = max(0, min(MAX_SCORE, score))
save_score(score)
present = score > 0
print("Current score:", score)
print("PRESENCE:", "YES" if present else "NO")
send_to_ha(present)
send_to_ha(score > 0)
finally:
safe_delete(TMP_IMG)
delay = get_delay(score)
print(f"[SLEEP] Next check in {delay}s")
time.sleep(delay)
time.sleep(get_delay(score))
if __name__ == "__main__":