M api_commands/SUNSET.py => api_commands/SUNSET.py +9 -1
@@ 53,8 53,13 @@ def change_brightness(brightness_level):
def power_off_lights():
for light in ALL_LIGHTS:
- #change color to red
home_ass_cmd(light, "turn_off")
+
+ # Trun off all swiched lights
+ for switch in ALL_SWITCHES:
+ if switch == HALL_LIGHTS or switch == STAR_CHARGER:
+ continue
+ home_ass_cmd(switch, "turn_off")
#Block lights
@@ 62,6 67,8 @@ with open("/tmp/light-blocking-SUNSET", "w+") as fh:
pass
#Turn on night sky
+home_ass_cmd(LIVING_ROOM_LIGHTS, "turn_off")
+home_ass_cmd(HALL_LIGHTS, "turn_on")
home_ass_cmd(STAR_CHARGER, "turn_on")
change_lights(color, total_time)
@@ 77,3 84,4 @@ power_off_lights()
#Turn on night sky
time.sleep(120)
home_ass_cmd(STAR_CHARGER, "turn_off")
+home_ass_cmd(HALL_LIGHTS, "turn_off")
A api_commands/smart_power.py => api_commands/smart_power.py +62 -0
@@ 0,0 1,62 @@
+#!/usr/bin/python3
+#home-butler
+#Copyright (C) 2022 David Hamner
+
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with this program. If not, see <http://www.gnu.org>
+
+#import butler stuff
+from butler_common import *
+import glob
+
+if glob.glob("/tmp/*light-blocking*") != []:
+ print("Skipping smart power because of /tmp/*light-blocking*")
+ exit()
+
+living_room_occupancy = home_ass_data("binary_sensor.living_room_occupancy")["state"] == "on"
+kitchen_occupancy = home_ass_data("binary_sensor.kitchen_occupancy")["state"] == "on"
+bathroom_occupancy = home_ass_data("binary_sensor.bathroom_occupancy")["state"] == "on"
+bathroom_quick_sensor = home_ass_data("binary_sensor.bathroom_quick")["state"] == "on"
+
+print("INFO: ")
+print([living_room_occupancy, kitchen_occupancy, bathroom_occupancy, bathroom_quick_sensor])
+
+
+def trun_on(devices):
+ for device in devices:
+ if device in ALL_LIGHTS:
+ home_ass_cmd(device, "turn_on", entity_data='"brightness": 254, "color_temp": 185')
+ else:
+ home_ass_cmd(device, "turn_on")
+
+def trun_off(devices):
+ for device in devices:
+ home_ass_cmd(device, "turn_off")
+
+# Kitchen
+if kitchen_occupancy:
+ trun_on(KITCHEN_ON_USER)
+else:
+ trun_off(KITCHEN_ON_USER)
+
+# Bathroom
+if bathroom_quick_sensor or bathroom_occupancy:
+ trun_on(BATHROOM_ON_USER)
+else:
+ trun_off(BATHROOM_ON_USER)
+
+# Living room
+if living_room_occupancy:
+ trun_on(LIVING_ROOM_ON_USER)
+else:
+ trun_off(LIVING_ROOM_ON_USER)
A api_commands/you_have_mail.py => api_commands/you_have_mail.py +52 -0
@@ 0,0 1,52 @@
+#!/usr/bin/python3
+#home-butler
+#Copyright (C) 2022 David Hamner
+
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with this program. If not, see <http://www.gnu.org/licenses/>.
+import time
+import datetime
+import os
+import glob
+
+#import butler stuff
+from butler_common import *
+#home_ass_cmd("switch.red_alert_on_off", "turn_off")
+
+mail_note_file = "/tmp/light-blocking_you_have_mail.txt"
+
+if os.path.isfile(mail_note_file):
+ print("You know you already have mail")
+ exit()
+
+os.mknod(mail_note_file)
+
+def change_lights(color, brightness_level):
+ for light in MAIL_LIGHTS:
+ #change color to red
+ home_ass_cmd(light, "turn_on", entity_data='"brightness": ' + str(brightness_level))
+ home_ass_cmd(light, "turn_on", entity_data='"rgb_color": ' + str(color))
+
+keep_running = True
+while keep_running:
+ say("You have mail")
+ for i in range (0,30):
+ if not os.path.isfile(mail_note_file):
+ keep_running = False
+ break
+ change_lights([0,0,255], 254)
+ time.sleep(1.5)
+ change_lights([100,0,150], 254)
+ time.sleep(1.5)
+
+change_lights([100,0,150], 0)
M background_scripts/smart_temp.py => background_scripts/smart_temp.py +35 -11
@@ 20,27 20,51 @@ import os
#import butler stuff
from butler_common import *
+#ideal_temp = 69
+#temp_buffer = 2
+humidity_trigger = 56
-ideal_temp = 69
-temp_buffer = 2
while True:
#only run every X
time.sleep(10)
#Get data from home ass
- temp_in_room = float(home_ass_data("sensor.sensor_brick_temperature")['state'])
- weather_outside = home_ass_data("weather.home")["attributes"]
- temp_outside = float(weather_outside["temperature"])
- #bedroom_window = home_ass_data("binary_sensor.window_ias_zone")['state']
+ temp_in_bathroom = float(home_ass_data("sensor.sensor_brick_temperature")['state'])
+ humidity_in_bathroom = float(home_ass_data("sensor.sensor_brick_humidity")['state'])
+ #weather_outside = home_ass_data("weather.home")["attributes"]
+ #temp_outside = float(weather_outside["temperature"])
+ #bathroom_window = home_ass_data("binary_sensor.window_ias_zone")['state']
#Debug info:
- print(f"temp_in_room: {temp_in_room}")
- print(f"temp_outside: {temp_outside}")
+ print(f"temp_in_bathroom: {temp_in_bathroom}")
+ print(f"humidity_in_bathroom: {humidity_in_bathroom}")
+ if humidity_in_bathroom > humidity_trigger:
+ #Check if switch.bathroom_dehumidifier_on_off is on, like it should be
+ dehumidifier_state = home_ass_data("switch.bathroom_dehumidifier_on_off")["state"]
+ if dehumidifier_state == "on":
+ print(f"Dehumidifier still running: {humidity_in_bathroom} > {humidity_trigger}")
+ else:
+ msg = f"Powering on dehumidifier, bathroom humidity at {humidity_in_bathroom}%"
+ print(msg)
+ say(msg)
+ home_ass_cmd("switch.bathroom_dehumidifier_on_off", "turn_on")
+ else:
+ #Check if switch.bathroom_dehumidifier_on_off is off, like it should be
+ dehumidifier_state = home_ass_data("switch.bathroom_dehumidifier_on_off")["state"]
+ if dehumidifier_state == "off":
+ print(f"Dehumidifier OFF: {humidity_in_bathroom} < {humidity_trigger}")
+ else:
+ msg = f"Powering off dehumidifier, bathroom humidity at {humidity_in_bathroom}%"
+ print(msg)
+ say(msg)
+ home_ass_cmd("switch.bathroom_dehumidifier_on_off", "turn_off")
+ """
+ #Old stuff for a room fan, might reuse at some point
#Check if window fan should be on
- if temp_in_room + temp_buffer > ideal_temp:
- if temp_outside < temp_in_room:
+ if temp_in_bathroom + temp_buffer > ideal_temp:
+ if temp_outside < temp_in_bathroom:
print("Window fan should be on")
#check fan state
fan_state = home_ass_data("switch.window_fan_on_off")["state"]
@@ 58,4 82,4 @@ while True:
home_ass_cmd("switch.window_fan_on_off", "turn_off")
else:
print("Good thing it already is... :)")
-
+ """
M background_scripts/wand_connect.py => background_scripts/wand_connect.py +2 -2
@@ 61,7 61,7 @@ def nox():
def alohomora():
cmd = "espeak 'alohomora'"
os.system(cmd)
- home_ass_cmd("switch.robo_printers_on_off", "turn_on")
+ home_ass_cmd(PRINTERS, "turn_on")
#switch.robo_printers_on_off
def geminio():
@@ 73,7 73,7 @@ def geminio():
def quietus():
cmd = "espeak 'kwy uh tus'"
os.system(cmd)
- home_ass_cmd("switch.robo_printers_on_off", "turn_off")
+ home_ass_cmd(PRINTERS, "turn_off")
def finite_incantatem():
cmd = "espeak 'fi nee tay in can tah tem'"
A background_scripts/watch_sensors.py => background_scripts/watch_sensors.py +83 -0
@@ 0,0 1,83 @@
+#!/usr/bin/python3
+import os
+import time
+import glob
+from watchdog.observers import Observer
+from watchdog.events import LoggingEventHandler
+
+from butler_common import *
+
+script_path = os.path.realpath(os.path.abspath(__file__))
+script_dir = os.path.dirname(script_path)
+
+security_sensors_event_root = "/".join(script_dir.split("/")[:-1])
+security_sensors_event_root = f"{security_sensors_event_root}/security_sensors/events"
+
+running_cameras = [entry.path for entry in os.scandir(security_sensors_event_root) if entry.is_dir()]
+print(running_cameras)
+
+
+
+current_event = ""
+stuff_found = {}
+def test(stuff):
+ global current_event
+ path = stuff.src_path
+ #print(path)
+
+ event = path.split("/")[-2]
+ #print(path)
+ if path.endswith(".jpg"):
+ image_name = path.split("/")[-1].split(".")[0]
+ image_name = "/".join(path.split("/")[:-1]) + "/*" + image_name + "*"
+ #print(f"Debug: {image_name}")
+
+ #delay for AI
+ time.sleep(.1)
+ image_name = glob.glob(image_name)[0]
+ image_name = image_name.split("/")[-1]
+ event_time = path.split("/")[-2]
+ location = path.split("/")[-3]
+ event_tag = f"{location}_{event_time}"
+ #print(image_name, location)
+ if "_" in image_name:
+ objects = image_name.split("_")[:-1]
+ if objects != []:
+ if event_tag != current_event:
+ current_event = event_tag
+ obj_str = " ".join(objects)
+ say(f"{obj_str} detected at {location}")
+ #TODO log
+ log(f"motion:{obj_str} at {location}")
+ stuff_found[event_tag] = objects
+ else:
+ for obj in objects:
+ if obj not in stuff_found[event_tag]:
+ stuff_found[event_tag].append(obj)
+ say(f"Possibly a {obj} too.")
+
+ print(f"Found {objects} at {location}")
+
+
+def watch_folder(dev_file, handle_file_change, init_value=None, user_editable=True):
+ if handle_file_change != None:
+ observer = Observer()
+ #objects suck hack.
+ handle_file_change.dispatch = handle_file_change
+ observer.schedule(handle_file_change, dev_file, recursive=True)
+ observer.start()
+
+log_files = glob.glob("/tmp/*motion*watching*")
+def log(msg):
+ if log_files != []:
+ for logfile in log_files:
+ with open(logfile, 'a+') as fh:
+ if not msg.endswith("\n"):
+ msg = msg + '\n'
+ fh.write(msg)
+
+
+watch_folder(running_cameras[0], test)
+
+while True:
+ time.sleep(1)
A security_sensors/sensors.py => security_sensors/sensors.py +328 -0
@@ 0,0 1,328 @@
+#!/usr/bin/python3
+#home-butler
+#GNU LGPL v3
+#Copyright (C) 2022 David Hamner
+#hamner@librem.one
+
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Lesser General Public
+#License as published by the Free Software Foundation; either
+#version 3 of the License, or (at your option) any later version.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#Lesser General Public License for more details.
+
+#You should have received a copy of the GNU Lesser General Public License
+#along with this program; if not, write to the Free Software Foundation,
+#Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import cv2
+import numpy as np
+from datetime import datetime
+import time
+from threading import Thread
+import subprocess
+import os
+import json
+from signal import signal, SIGINT
+from flask_opencv_streamer.streamer import Streamer
+import torch, detectron2
+TORCH_VERSION = ".".join(torch.__version__.split(".")[:2])
+CUDA_VERSION = torch.__version__.split("+")[-1]
+print("torch: ", TORCH_VERSION, "; cuda: ", CUDA_VERSION)
+print("detectron2:", detectron2.__version__)
+import detectron2
+from detectron2.utils.logger import setup_logger
+setup_logger()
+# import some common libraries
+import numpy as np
+import os, json, cv2, random
+# import some common detectron2 utilities
+from detectron2 import model_zoo
+from detectron2.engine import DefaultPredictor
+from detectron2.config import get_cfg
+from detectron2.utils.visualizer import Visualizer
+from detectron2.data import MetadataCatalog, DatasetCatalog
+cfg = get_cfg()
+# add project-specific config (e.g., TensorMask) here if you're not running a model in detectron2's core library
+cfg.merge_from_file(model_zoo.get_config_file("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"))
+cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5 # set threshold for this model
+# Find a model from detectron2's model zoo. You can use the https://dl.fbaipublicfiles... url as well
+cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml")
+predictor = DefaultPredictor(cfg)
+
+
+port = 2468
+require_login = False
+
+
+#Thank you to: https://github.com/abhishek305/Motion-capture-using-opencv-contours/blob/master/Contours%20Opencv.py
+#Some of this was based on ^^^
+script_path = os.path.realpath(os.path.abspath(__file__))
+script_dir = os.path.dirname(script_path)
+event_base = f"{script_dir}/events/"
+if not os.path.isdir(event_base):
+ os.mkdir(event_base)
+
+#Main settings
+boarder = 70
+num_frames_needed_to_trigger = 3
+num_frames_needed_to_end_trigger = 10
+min_move_area = 600
+triger_move_area = 12000
+too_many_motion_points = 400
+
+max_back_log = 3
+eyes_busy = False
+
+
+#cam_devices= {"Potato": "rtsp://10.250.10.225:554/Streaming/Channes/ID/?transportmode=unicast"}
+#cap=cv2.VideoCapture("rtsp://10.250.10.225:554/Streaming/Channes/ID/?transportmode=unicast")
+
+exit = False
+
+
+def load_settings():
+ cam_devices = {}
+ settings_file_path = f"{script_dir}/settings.txt"
+ with open(settings_file_path) as settings_file:
+ for config_line in settings_file.readlines():
+ if config_line.startswith("#") or config_line.strip() == "":
+ continue
+ raw_data = config_line.strip().split("~")
+ name = raw_data[0]
+ device = raw_data[1]
+ cam_devices[name] = device
+ return(cam_devices)
+cam_devices = load_settings()
+print(cam_devices)
+
+
+def what_is_this(file_name):
+ global eyes_busy
+ global predictor
+ eyes_busy = True
+ found_objs = []
+
+ im = cv2.imread(file_name)
+
+ with torch.no_grad():
+ outputs = predictor(im)
+ instances = outputs["instances"]
+ detected_class_indexes = instances.pred_classes
+ prediction_boxes = instances.pred_boxes
+ metadata = MetadataCatalog.get(cfg.DATASETS.TRAIN[0])
+ class_catalog = metadata.thing_classes
+
+ #Thank you to: https://stackoverflow.com/a/68471952
+ for idx, coordinates in enumerate(prediction_boxes):
+ class_index = detected_class_indexes[idx]
+ class_name = class_catalog[class_index]
+ found_objs.append(class_name)
+ print(class_name, coordinates)
+
+ #Clean up ram
+ #del(predictor)
+ del(outputs)
+ del(instances)
+ del(detected_class_indexes)
+ del(prediction_boxes)
+ del(metadata)
+ del(class_catalog)
+
+ eyes_busy = False
+
+ #cmd = f"lumi predict --checkpoint=fast".split(" ")
+ #cmd.append(file_name)
+ #OUT = subprocess.Popen(cmd, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
+ #stdout,stderr = OUT.communicate()
+ #stdout = stdout.decode().strip()
+ #if "\n" in stdout:
+ # raw_text = stdout.split('\n')[-1]
+ # result = json.loads(raw_text)
+ # eyes_busy = False
+ # for obj in result['objects']:
+ # name = obj['label']
+ # if name not in found_objs:
+ # found_objs.append(name)
+ if found_objs != []:
+ new_name = "_".join(found_objs)
+ old_name = file_name.split('/')[-1]
+ full_new_name = os.path.dirname(file_name)
+ full_new_name = f"{full_new_name}/{new_name}_{old_name}"
+ os.rename(file_name, full_new_name)
+ print(found_objs)
+ return(found_objs)
+ #print(stderr)
+
+def main_cam_loop(cam_name, dev_name):
+ global exit
+ global port
+
+ streamer = Streamer(port, require_login)
+ print(f"Sarting {cam_name}, Dev: {dev_name}, on port: {port}")
+ port = port + 1
+
+ cap = cv2.VideoCapture(dev_name)
+ if cap.isOpened():
+ ret,frame = cap.read()
+ else:
+ ret =False
+ if not ret:
+ print(f"Error with {dev_name} {cam_name}")
+ return
+ cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
+ cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
+
+ ret,frame1 = cap.read()
+ ret,frame2 = cap.read()
+
+ back_log = []
+ move_frames = 0
+ event_path = ""
+ sleepy_frames = 0 # used to keep track of number of non moving frames
+ video_to_save = ""
+ fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
+ start_time = time.time()
+ frame_count = 0
+ fps = 1
+
+
+ while not exit:
+ ret,frame = cap.read()
+ #frame = cv2.fastNlMeansDenoisingColored(frame,None,10,10,7,21)
+ time_now = time.time()
+ time_taken = time_now - start_time
+ start_time = time_now
+ fps = 1/time_taken
+ print(f"{cam_name} FPS: {fps}")
+ d=cv2.absdiff(frame1,frame2)
+ try:
+ grey=cv2.cvtColor(d,cv2.COLOR_BGR2GRAY)
+ except Exception:
+ print("Error pulling frame from...")
+ continue
+ blur =cv2.GaussianBlur(grey,(5,5),0)
+ ret,th=cv2.threshold(blur,20,255,cv2.THRESH_BINARY)
+ dilated=cv2.dilate(th,np.ones((3,3),np.uint8),iterations=3)
+ #img,c,h=cv2.findContours(dilated,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
+ contours, hierarchy = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
+
+
+ found_moving = False
+ found_boxes = 0
+ for i in contours:
+ if cv2.contourArea(i) < min_move_area:
+ continue
+ found_boxes = found_boxes + 1
+ if found_boxes > too_many_motion_points:
+ print(f"Warning, Too much moving! {found_boxes}")
+ continue
+ (x, y, w, h) = cv2.boundingRect(i)
+ size = w * h
+
+ if size > triger_move_area:
+ found_moving = True
+ if move_frames > num_frames_needed_to_trigger:
+ ts = time.strftime('%l:%M%p_(+%S)_on_%b_%d_%Y')
+ local_event_base = f"{event_base}/{cam_name}"
+ if not os.path.isdir(local_event_base):
+ os.mkdir(local_event_base)
+ #ts = f"{ }_at_{ts}"
+ if event_path == "":
+ event_path = f"{local_event_base}/{ts}/"
+ print(f"New event: {ts}")
+ os.mkdir(event_path)
+ #video_to_save = cv2.VideoWriter(f'{script_dir}/{ts}/event.avi', -1, 20.0, (1920,1080))
+ height, width = frame1.shape[:2]
+ shape = (width, height)
+ video_to_save = cv2.VideoWriter(f'{local_event_base}/{ts}/event.mp4', fourcc, fps, shape)
+ cv2.rectangle(frame1, (x-boarder, y-boarder), (x + w+boarder, y + h+boarder), (0, 0, 255), 2)
+
+ #expand boarder around moving item
+ x_start = x-boarder
+ if x_start < 0:
+ x_start = 0
+ x_end = x + w+boarder
+ if x_end > frame1.shape[1]:
+ x_end = frame1.shape[1]
+ y_start = y-boarder
+ if y_start < 0:
+ y_start = 0
+ y_end = y + h+boarder
+ if y_end > frame1.shape[0]:
+ y_end = frame1.shape[0]
+
+
+ #crop = frame1[50:100, 1620:1920]
+ #print("Shape of the cut", crop.shape)
+
+ #If eye thread is not busy, check what is moving
+ #TODO If busy for too long do it anyway.
+ if not eyes_busy:
+ crop = frame[y_start:y_end, x_start:x_end]
+ file_name = f"{event_path}{move_frames}.jpg"
+ cv2.imwrite(file_name, crop)
+ #cv2.imshow("cropped", crop)
+ back_log.append(Thread(target = what_is_this, args=[file_name]))
+ back_log[-1].setDaemon(True)
+ back_log[-1].start()
+ else:
+ print("Warning, skiping process")
+ #Movment but not a triger yet
+ else:
+ cv2.rectangle(frame1, (x-boarder, y-boarder), (x + w+boarder, y + h+boarder), (255, 0, 0), 2)
+
+ #Save frame to video
+ print(video_to_save)
+ if video_to_save != "":
+ video_to_save.write(frame1)
+
+ streamer.update_frame(frame1)
+ if not streamer.is_streaming:
+ streamer.start_streaming()
+
+ if found_moving:
+ move_frames = move_frames + 1
+ else:
+ #End of event
+ if sleepy_frames > num_frames_needed_to_end_trigger:
+ move_frames = 0
+ event_path = ""
+ sleepy_frames = 0
+ if video_to_save != "":
+ video_to_save.release()
+ video_to_save = ""
+ else:
+ sleepy_frames = sleepy_frames + 1
+
+ cv2.drawContours(frame1,contours,-1,(0,255,255),2)
+ #cv2.imshow("inter",frame1)
+
+ if cv2.waitKey(40) == 27:
+ exit = True
+ frame1 = frame2
+ ret,frame2= cap.read()
+ cv2.destroyAllWindows()
+ #VideoFileOutput.release()
+ cap.release()
+
+cam_threads = []
+for cap_dev in cam_devices:
+ print(f"Starting {cap_dev} {cam_devices[cap_dev]}")
+ cam_threads.append(Thread(target=main_cam_loop, args=[cap_dev, cam_devices[cap_dev]]))
+ cam_threads[-1].setDaemon(True)
+ cam_threads[-1].start()
+ time.sleep(3)
+
+def on_exit(signal_received, frame):
+ global exit
+ exit = True
+ # Handle any cleanup here
+ print('SIGINT or CTRL-C detected. Exiting gracefully')
+
+signal(SIGINT, on_exit)
+while not exit:
+ time.sleep(1)
A security_sensors/settings.txt => security_sensors/settings.txt +7 -0
@@ 0,0 1,7 @@
+#Name~Device
+#Door~rtsp://10.250.10.225:554/Streaming/Channes/ID/?transportmode=unicast
+Front_Door~/dev/video0
+#Studio_Window~/dev/video2
+#Window~4
+#Shed~2
+#test~1
A security_sensors/setup.txt => security_sensors/setup.txt +22 -0
@@ 0,0 1,22 @@
+https://github.com/facebookresearch/detectron2/blob/main/INSTALL.md
+sudo pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113
+sudo python3 -m pip install 'git+https://github.com/facebookresearch/detectron2.git'
+
+#sudo pip3 install cython; pip install -U 'git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI'
+
+#Old
+Install pip2
+#CPU
+sudo pip2 install "tensorflow<2.0"
+sudo pip2 install luminoth
+
+#GPU
+sudo pip2 install "tensorflow-gpu<2.0"
+sudo pip2 install luminoth[tf-gpu]
+
+lumi checkpoint refresh
+lumi checkpoint list
+lumi checkpoint download accurate
+lumi checkpoint download fast
+
+lumi predict --checkpoint=fast <Img>
A security_sensors/startup.sh => security_sensors/startup.sh +5 -0
@@ 0,0 1,5 @@
+#!/bin/bash
+#Limit memory use
+cd "$(dirname "$0")"
+export LRU_CACHE_CAPACITY=1
+LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 python3 ./sensors.py
A security_sensors/templates/fail.html => security_sensors/templates/fail.html +15 -0
@@ 0,0 1,15 @@
+<!doctype html>
+<html>
+
+<body>
+ <div id="reason" style="display: none;">
+ {{reason}}
+ </div>
+
+ <script>
+ alert(document.getElementById("reason").innerText);
+ location.href = "../change%20password";
+ </script>
+</body>
+
+</html><
\ No newline at end of file
A security_sensors/templates/form.html => security_sensors/templates/form.html +33 -0
@@ 0,0 1,33 @@
+<!doctype html>
+<html>
+
+<body>
+ <center>
+ <form action="../change%20password%20result" method="POST">
+ <table>
+ <tr>
+ <th>Username:</th>
+ <th><input type="text" name="username" /></th>
+ </tr>
+ <tr>
+ <th>Old Password</th>
+ <th><input type="password" name="old_pw" /></th>
+ </tr>
+ <tr>
+ <th>New Password</th>
+ <th><input type="password" name="pw" /></th>
+ </tr>
+ <tr>
+ <th>(Confirm)</th>
+ <th><input type="password" name="pw_conf" /></th>
+ </tr>
+ <tr>
+ <th><input type="submit" value="Change Password" /></th>
+ <th></th>
+ </tr>
+ </table>
+ </form>
+ </center>
+</body>
+
+</html><
\ No newline at end of file
A security_sensors/templates/index.html => security_sensors/templates/index.html +9 -0
@@ 0,0 1,9 @@
+<html>
+ <head>
+ <title>Video Streaming Demonstration</title>
+ </head>
+ <body>
+ <h1>Video Streaming Demonstration</h1>
+ <img src="{{ url_for('video_feed') }}">
+ </body>
+</html>
A security_sensors/templates/pass.html => security_sensors/templates/pass.html +15 -0
@@ 0,0 1,15 @@
+<!doctype html>
+<html>
+
+<body>
+ <script>
+ alert("Sucessfully changed password!");
+ location.href = "..";
+ </script>
+
+ <p>
+ {{reason}}
+ </p>
+</body>
+
+</html><
\ No newline at end of file
M voice_commands/all clear_green alert.py => voice_commands/all clear_green alert.py +1 -0
@@ 23,6 23,7 @@ home_ass_cmd(STAR_CHARGER, "turn_off")
#Stop any blocking scripts
os.system("rm /tmp/light-blocking*")
+os.system("rm /tmp/*watching-shields")
time.sleep(.4)
#Trun on all lights
M voice_commands/movie mode.py => voice_commands/movie mode.py +11 -0
@@ 20,15 20,26 @@ from butler_common import *
home_ass_cmd("switch.red_alert_on_off", "turn_off")
home_ass_cmd(STAR_CHARGER, "turn_off")
+home_ass_cmd("switch.hallway_lights_on_off", "turn_on")
+home_ass_cmd("switch.living_room_toplights_on_off", "turn_off")
+
+
+#Trun off all colored lights
for light in ALL_LIGHTS:
if light in TOP_LIGHTS:
continue
home_ass_cmd(light, "turn_off")
+#Trun on light.living_room_accent
+home_ass_cmd("light.living_room_accent", "turn_on", entity_data='"rgb_color": [100,0,150]')
+home_ass_cmd("light.living_room_accent", "turn_on", entity_data='"brightness": ' + str(150))
+"""
for light in TOP_LIGHTS:
#change color to red
#home_ass_cmd(light, "turn_on", entity_data='"rgb_color": [255,0,0]')
#set brightness and color_temp
home_ass_cmd(light, "turn_on", entity_data='"brightness": 22, "color_temp": 254')
+
os.system("firefox &")
+"""
A voice_commands/nintendo.py => voice_commands/nintendo.py +24 -0
@@ 0,0 1,24 @@
+#!/usr/bin/python3
+#home-butler
+#Copyright (C) 2022 David Hamner
+
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+import time
+import os
+#You should have received a copy of the GNU General Public License
+#along with this program. If not, see <http://www.gnu.org/licenses/>.
+#import butler stuff
+from butler_common import *
+
+# Trun on work lights
+say("Running WII emulator")
+home_ass_cmd(WII_LIGHT, "turn_on")
+os.system("dolphin-emu")
A voice_commands/shields down_computer report_lower shields.py => voice_commands/shields down_computer report_lower shields.py +121 -0
@@ 0,0 1,121 @@
+#!/usr/bin/python3
+#home-butler
+#Copyright (C) 2022 David Hamner
+
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+
+#You should have received a copy of the GNU General Public License
+#along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import time
+from datetime import datetime
+#import butler stuff
+from butler_common import *
+
+
+#if not os.path.isfile("/tmp/motion-voice-watching-shields"):
+# msg = "Shields are not running"
+# print(msg)
+# say(msg)
+ #exit()
+
+
+def change_lights(color, brightness_level):
+ for light in ALL_LIGHTS:
+ #change color to red
+ home_ass_cmd(light, "turn_on", entity_data='"brightness": ' + str(brightness_level))
+ home_ass_cmd(light, "turn_on", entity_data='"rgb_color": ' + str(color))
+
+def change_brightness(brightness_level):
+ for light in ALL_LIGHTS:
+ #change color to red
+ home_ass_cmd(light, "turn_on", entity_data='"brightness": ' + str(brightness_level))
+
+def power_off_lights():
+ for light in ALL_LIGHTS:
+ #change color to red
+ home_ass_cmd(light, "turn_off")
+
+#Thanks https://stackoverflow.com/a/1703705
+def watch(fn):
+ fp = open(fn, 'r')
+ while True:
+ if not os.path.isfile(fn):
+ return
+ new = fp.readline()
+ if new:
+ yield (new)
+ else:
+ time.sleep(0.5)
+
+
+# Trun on stuff
+home_ass_cmd("switch.living_room_toplights_on_off", "turn_on")
+
+shield_files = glob.glob("/tmp/*watching-shields*")
+shields_data = []
+for shield_file in shield_files:
+ with open(shield_file) as fh:
+ shields_data = shields_data + fh.readlines()
+mail_files = glob.glob("/tmp/*_you_have_mail.txt")
+if mail_files != []:
+ time_delivered = datetime.fromtimestamp(os.path.getctime(mail_files[0]))
+ time_delivered = time_delivered.strftime("%B %d, %T")
+else:
+ time_delivered = ""
+say("Welcome Home")
+# Stop any blocking scripts
+os.system("rm /tmp/light-blocking*")
+os.system("rm /tmp/*watching-shields")
+time.sleep(.4)
+
+
+if mail_files != []:
+ #time_delivered = ""
+ #for mail_file in mail_files:
+ # time_delivered = datetime.fromtimestamp(os.path.getctime(mail_file))
+ # time_delivered = time_delivered.strftime("%B %d, %T")
+ msg = f"Mail delivered at {time_delivered}"
+ # Trun off lights
+
+ time.sleep(3)
+ # Trun off all lights
+ for light in MAIL_LIGHTS:
+ #if light in TOP_LIGHTS:
+ # continue
+ #if light in OFFICE:
+ # continue
+ home_ass_cmd(light, "turn_off")
+ print(msg)
+ say(msg)
+ #for mail_file in mail_files:
+ #os.system(f"rm {mail_file}")
+ #exit()
+
+
+if shield_files != []:
+ number_events = {}
+ for line in shields_data:
+ event_type, data = line.split(":")
+ if event_type not in number_events:
+ number_events[event_type] = 1
+ else:
+ number_events[event_type] = number_events[event_type] + 1
+ msg = ""
+ for event_type in number_events:
+ msg = f"{msg} {number_events[event_type]} total {event_type} events"
+
+ msg = f"{msg} full report under tmp shields_last"
+ print(msg)
+ say(msg)
+
+ os.system("mv /tmp/motion-voice-watching-shields /tmp/shields_last")
+
M voice_commands/shields up.py => voice_commands/shields up.py +17 -6
@@ 19,6 19,7 @@ import time
#import butler stuff
from butler_common import *
+say("Powering up shields")
if os.path.isfile("/tmp/motion-voice-watching-shields"):
print("Shields already up")
@@ 40,6 41,11 @@ def change_brightness(brightness_level):
#change color to red
home_ass_cmd(light, "turn_on", entity_data='"brightness": ' + str(brightness_level))
+def power_off_lights():
+ for light in ALL_LIGHTS:
+ home_ass_cmd(light, "turn_off")
+ for switch in ALL_SWITCHES:
+ home_ass_cmd(switch, "turn_off")
#Thanks https://stackoverflow.com/a/1703705
def watch(fn):
@@ 53,13 59,18 @@ def watch(fn):
else:
time.sleep(0.5)
+
+# Trun off stuff
+power_off_lights()
+
for line in watch("/tmp/motion-voice-watching-shields"):
- print(f"Alert! {line}")
- home_ass_cmd("switch.red_alert_on_off", "turn_on")
- change_lights([255,0,0], 254)
- change_lights([0,0,255], 254)
- change_brightness(0)
- home_ass_cmd("switch.red_alert_on_off", "turn_off")
+ if not line.startswith("voice"):
+ say(f"Alert! {line}")
+ #home_ass_cmd("switch.red_alert_on_off", "turn_on")
+ #change_lights([255,0,0], 254)
+ #change_lights([0,0,255], 254)
+ #change_brightness(0)
+ #home_ass_cmd("switch.red_alert_on_off", "turn_off")
#cmd = "espeak 'Warning Shields are up'"
#os.system(cmd)
#time.sleep(30)
A voice_commands/snitches get stitches.py => voice_commands/snitches get stitches.py +23 -0
@@ 0,0 1,23 @@
+#!/usr/bin/python3
+#home-butler
+#Copyright (C) 2022 David Hamner
+
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU General Public License as published by
+#the Free Software Foundation, either version 3 of the License, or
+#(at your option) any later version.
+
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+import time
+import os
+#You should have received a copy of the GNU General Public License
+#along with this program. If not, see <http://www.gnu.org/licenses/>.
+#import butler stuff
+from butler_common import *
+
+# Trun on work lights
+say("Powering on sewing machine")
+home_ass_cmd(SEWING_MACHINE, "turn_on")
M voice_commands/studio mode.py => voice_commands/studio mode.py +2 -2
@@ 24,9 24,9 @@ home_ass_cmd(STAR_CHARGER, "turn_off")
os.system("rm /tmp/light-blocking*")
time.sleep(.4)
#Max power
-home_ass_cmd(OFFICE_TABLE_LIGHTS[0], "turn_on", entity_data='"brightness": 254, "color_temp": 381')
+#home_ass_cmd(OFFICE_TABLE_LIGHTS[0], "turn_on", entity_data='"brightness": 254, "color_temp": 381')
#Set color
-home_ass_cmd(OFFICE_TABLE_LIGHTS[0], "turn_on", entity_data='"rgb_color": [0,0,255]')
+#home_ass_cmd(OFFICE_TABLE_LIGHTS[0], "turn_on", entity_data='"rgb_color": [0,0,255]')
#Max power
home_ass_cmd(OFFICE_SMALL_LAMP[0], "turn_on", entity_data='"brightness": 254, "color_temp": 381')
M voice_commands/workshop_work shop.py => voice_commands/workshop_work shop.py +17 -11
@@ 18,25 18,31 @@ import os
#import butler stuff
from butler_common import *
-home_ass_cmd("switch.red_alert_on_off", "turn_off")
-home_ass_cmd(STAR_CHARGER, "turn_off")
-#Stop any blocking scripts
+# Trun on work lights
+home_ass_cmd(LIVING_ROOM_LIGHTS, "turn_on")
+
+# Stop any blocking scripts
os.system("rm /tmp/light-blocking*")
+os.system("rm /tmp/*watching-shields")
time.sleep(.4)
+# Trun off all swiched lights
+for switch in ALL_SWITCHES:
+ if switch == LIVING_ROOM_LIGHTS:
+ continue
+ home_ass_cmd(switch, "turn_off")
-current_voice_cmd = ""
-
+# Trun off all lights
for light in ALL_LIGHTS:
- if light in TOP_LIGHTS:
- continue
- if light in OFFICE:
- continue
+ #if light in TOP_LIGHTS:
+ # continue
+ #if light in OFFICE:
+ # continue
home_ass_cmd(light, "turn_off")
-for light in TOP_LIGHTS:
+#for light in TOP_LIGHTS:
#change color to red
#home_ass_cmd(light, "turn_on", entity_data='"rgb_color": [255,0,0]')
#set brightness and color_temp
- home_ass_cmd(light, "turn_on", entity_data='"brightness": 254, "color_temp": 185')
+ #home_ass_cmd(light, "turn_on", entity_data='"brightness": 254, "color_temp": 185')