~hamner/HomeButler

5bdd9eaac14e1d19e019c4efff64688f690cbc87 — David Hamner 1 year, 8 months ago d81be9c
Just moved update
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')