Spaces:
Sleeping
Sleeping
Upload 8 files
Browse files- app.py +77 -0
- camesa.py +30 -0
- config.py +3 -0
- device_blueprints.py +85 -0
- device_core.py +156 -0
- device_routes.py +20 -0
- models.py +27 -0
- requirements.txt +6 -0
app.py
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Flask, render_template, request, jsonify, Response
|
2 |
+
from flask_bootstrap import Bootstrap
|
3 |
+
from flask_sqlalchemy import SQLAlchemy
|
4 |
+
from datetime import datetime
|
5 |
+
import json
|
6 |
+
import plotly
|
7 |
+
import requests
|
8 |
+
from device_core import Camera, Sensor
|
9 |
+
from models import init_db, Device
|
10 |
+
from device_routes import SENSORS, CAMERAS, LIGHTING_DATA
|
11 |
+
from config import Config
|
12 |
+
from flask import Flask, render_template, jsonify
|
13 |
+
from device_blueprints import sensor_blueprint, camera_blueprint
|
14 |
+
|
15 |
+
app = Flask(__name__)
|
16 |
+
app.config.from_object(Config)
|
17 |
+
Bootstrap(app)
|
18 |
+
init_db(app)
|
19 |
+
|
20 |
+
# from main import app, db, Device
|
21 |
+
# app.app_context().push()
|
22 |
+
# db.create_all()
|
23 |
+
|
24 |
+
app.register_blueprint(sensor_blueprint, url_prefix='/sensors')
|
25 |
+
app.register_blueprint(camera_blueprint, url_prefix='/cameras')
|
26 |
+
|
27 |
+
# ---------------------------------------------------------------------------------------------
|
28 |
+
|
29 |
+
@app.route('/')
|
30 |
+
def home():
|
31 |
+
# Filter sensors based on their types
|
32 |
+
thermo = [sensor for sensor in SENSORS if sensor.id.startswith('t')]
|
33 |
+
ele = [sensor for sensor in SENSORS if sensor.id.startswith('e')]
|
34 |
+
|
35 |
+
# Render the index template with the plot data and sensor lists
|
36 |
+
return render_template('index.html', thermo=thermo, ele=ele, cameras=CAMERAS)
|
37 |
+
|
38 |
+
|
39 |
+
@app.route('/lighting-data/', methods=['POST', 'GET'])
|
40 |
+
def receive_data():
|
41 |
+
global LIGHTING_DATA
|
42 |
+
print(LIGHTING_DATA)
|
43 |
+
|
44 |
+
try:
|
45 |
+
if request.method == 'POST':
|
46 |
+
address_value = request.json.get('address')
|
47 |
+
intensity_value = request.json.get('value')
|
48 |
+
|
49 |
+
if intensity_value is None:
|
50 |
+
return jsonify({"error": "Value not provided"}), 400
|
51 |
+
if address_value is None:
|
52 |
+
return jsonify({"error": "Address not provided"}), 400
|
53 |
+
address_value = int(address_value)
|
54 |
+
|
55 |
+
LIGHTING_DATA[address_value] = intensity_value
|
56 |
+
|
57 |
+
response = requests.post(f"http://127.0.0.1:1880/", json={"daliData_flask":[address_value, intensity_value]}, timeout=1)
|
58 |
+
response.raise_for_status()
|
59 |
+
|
60 |
+
return jsonify({"address": address_value, "value": intensity_value}), 200
|
61 |
+
|
62 |
+
elif request.method == 'GET':
|
63 |
+
address_value = request.args.get('address')
|
64 |
+
if not address_value:
|
65 |
+
return jsonify({"error": "Address not provided"}), 400
|
66 |
+
address_value = int(address_value)
|
67 |
+
intensity_value = LIGHTING_DATA.get(address_value, 0)
|
68 |
+
|
69 |
+
return jsonify({"address": address_value, "value": intensity_value}), 200
|
70 |
+
|
71 |
+
except requests.RequestException as e:
|
72 |
+
print("debug")
|
73 |
+
return jsonify({"error": str(e)}), 500
|
74 |
+
|
75 |
+
|
76 |
+
if __name__ == '__main__':
|
77 |
+
app.run(host='localhost', port=5000, debug=True)
|
camesa.py
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
|
3 |
+
# Replace with your camera's RTSP URL
|
4 |
+
rtsp_url = 'rtsp://169.254.0.99:554/live.sdp'
|
5 |
+
rtsp_url = 0
|
6 |
+
|
7 |
+
# Create a VideoCapture object
|
8 |
+
cap = cv2.VideoCapture(rtsp_url)
|
9 |
+
|
10 |
+
while True:
|
11 |
+
# Capture frame-by-frame
|
12 |
+
ret, frame = cap.read()
|
13 |
+
|
14 |
+
# If frame is read correctly, ret is True
|
15 |
+
if not ret:
|
16 |
+
print("Can't receive frame (stream end?). Exiting ...")
|
17 |
+
break
|
18 |
+
|
19 |
+
# Display the resulting frame
|
20 |
+
cv2.imshow('IP Camera stream', frame)
|
21 |
+
|
22 |
+
# Press 'q' on keyboard to exit
|
23 |
+
if cv2.waitKey(1) == ord('q'):
|
24 |
+
break
|
25 |
+
|
26 |
+
# When everything done, release the VideoCapture object
|
27 |
+
cap.release()
|
28 |
+
|
29 |
+
# Close all OpenCV windows
|
30 |
+
cv2.destroyAllWindows()
|
config.py
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
class Config:
|
2 |
+
SQLALCHEMY_DATABASE_URI = 'sqlite:///devices.db'
|
3 |
+
# Other configurations
|
device_blueprints.py
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Blueprint, render_template, jsonify, request, Response
|
2 |
+
from models import Device, db # Assuming Device is your database model from models.py
|
3 |
+
from device_core import Sensor, Camera # Import your Sensor class
|
4 |
+
from device_routes import SENSORS, CAMERAS # Import your SENSORS list
|
5 |
+
import json
|
6 |
+
import plotly
|
7 |
+
|
8 |
+
sensor_blueprint = Blueprint('sensor_blueprint', __name__, template_folder='templates')
|
9 |
+
camera_blueprint = Blueprint('camera_blueprint', __name__, template_folder='templates')
|
10 |
+
|
11 |
+
@sensor_blueprint.route('/plot/<sensor_id>')
|
12 |
+
def generate_plot(sensor_id):
|
13 |
+
sensor = Sensor.find(SENSORS, sensor_id) # Assuming SENSORS is accessible here
|
14 |
+
if not sensor:
|
15 |
+
return jsonify({"error": "Sensor not found"}), 404
|
16 |
+
|
17 |
+
fig = sensor.plot(Device, title=sensor.name)
|
18 |
+
return json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)
|
19 |
+
|
20 |
+
|
21 |
+
@sensor_blueprint.route('/postplain/', methods=['POST'])
|
22 |
+
def post_plain():
|
23 |
+
data = request.get_json()
|
24 |
+
if not data:
|
25 |
+
return jsonify({'message': 'No input data provided'}), 400
|
26 |
+
|
27 |
+
device_id = data.get('id')
|
28 |
+
device_values = data.get('values')
|
29 |
+
device_units = data.get('units')
|
30 |
+
|
31 |
+
if device_values and device_id:
|
32 |
+
sensor = Sensor.find(SENSORS, device_id)
|
33 |
+
if sensor:
|
34 |
+
sensor.update_values(device_values, device_units)
|
35 |
+
|
36 |
+
device = Device(
|
37 |
+
sensor_id=device_id,
|
38 |
+
name=sensor.name,
|
39 |
+
time=sensor.time,
|
40 |
+
)
|
41 |
+
|
42 |
+
for i, value in enumerate(sensor.values):
|
43 |
+
setattr(device, f"value{i + 1}", value)
|
44 |
+
for i, unit in enumerate(sensor.units):
|
45 |
+
setattr(device, f"unit{i + 1}", unit)
|
46 |
+
|
47 |
+
db.session.add(device)
|
48 |
+
db.session.commit()
|
49 |
+
|
50 |
+
return jsonify({'message': 'Success!'}), 200
|
51 |
+
else:
|
52 |
+
return jsonify({'message': 'Sensor not found'}), 404
|
53 |
+
else:
|
54 |
+
return jsonify({'message': 'Missing data'}), 400
|
55 |
+
|
56 |
+
|
57 |
+
# -------------- CAMERA ROUTES --------------
|
58 |
+
|
59 |
+
|
60 |
+
@camera_blueprint.route('/video_feed/<camera_id>')
|
61 |
+
def stream_camera(camera_id):
|
62 |
+
"""
|
63 |
+
Stream the video feed from a specific camera.
|
64 |
+
"""
|
65 |
+
camera = next((cam for cam in CAMERAS if cam.id == camera_id), None)
|
66 |
+
if not camera:
|
67 |
+
return Response("Camera not found or could not be opened", status=404)
|
68 |
+
|
69 |
+
return Response(camera.gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
|
70 |
+
|
71 |
+
|
72 |
+
@camera_blueprint.route('/refresh_camera')
|
73 |
+
def refresh_camera():
|
74 |
+
success = True
|
75 |
+
message = "All cameras refreshed successfully."
|
76 |
+
|
77 |
+
for cam in CAMERAS:
|
78 |
+
cam.close_camera()
|
79 |
+
try:
|
80 |
+
cam.__init__(cam.id, cam.name, cam.index)
|
81 |
+
except:
|
82 |
+
success = False
|
83 |
+
message = "Error occurred while refreshing cameras."
|
84 |
+
|
85 |
+
return jsonify({"success": success, "message": message})
|
device_core.py
ADDED
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
from datetime import datetime, timedelta
|
3 |
+
from plotly.subplots import make_subplots
|
4 |
+
import plotly.graph_objects as go
|
5 |
+
import threading
|
6 |
+
import os
|
7 |
+
import time
|
8 |
+
|
9 |
+
class Camera:
|
10 |
+
def __init__(self, id, name, index=0, fps=1, save_duration=0.1, rtsp=None):
|
11 |
+
self.name = name
|
12 |
+
self.id = id
|
13 |
+
self.fps = fps
|
14 |
+
self.save_duration = save_duration
|
15 |
+
self.index = rtsp if rtsp else index
|
16 |
+
|
17 |
+
# Initialize camera in a separate thread to avoid blocking
|
18 |
+
self.out = None
|
19 |
+
self.cap = None
|
20 |
+
self.frame_width = 1980
|
21 |
+
self.frame_height = 720
|
22 |
+
self.frame_count = 0
|
23 |
+
self.start_time = time.time()
|
24 |
+
self.initialization_thread = threading.Thread(target=self.initialize_camera)
|
25 |
+
self.initialization_thread.start()
|
26 |
+
|
27 |
+
def initialize_camera(self):
|
28 |
+
self.cap = cv2.VideoCapture(self.index)
|
29 |
+
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.frame_width)
|
30 |
+
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.frame_height)
|
31 |
+
self.cap.set(cv2.CAP_PROP_FPS, self.fps)
|
32 |
+
|
33 |
+
# Wait for camera to open or timeout after 10 seconds
|
34 |
+
start_time = time.time()
|
35 |
+
while not self.cap.isOpened():
|
36 |
+
time.sleep(0.1)
|
37 |
+
if time.time() - start_time > 10: # 10 seconds timeout
|
38 |
+
print(f"Timeout: Unable to open camera {self.id} - {self.name}")
|
39 |
+
break
|
40 |
+
|
41 |
+
|
42 |
+
def save_frame(self, frame):
|
43 |
+
filename = "video/frames/frame_{}.jpg".format(self.frame_count)
|
44 |
+
cv2.imwrite(filename, frame)
|
45 |
+
self.frame_count += 1
|
46 |
+
|
47 |
+
def create_video_from_frames(self):
|
48 |
+
filename = "video/outpy_{}.mp4".format(time.strftime("%Y%m%d-%H%M%S"))
|
49 |
+
self.out = cv2.VideoWriter(filename, cv2.VideoWriter_fourcc(*'mp4v'), self.fps, (self.frame_width, self.frame_height))
|
50 |
+
for i in range(self.frame_count):
|
51 |
+
frame = cv2.imread("video/frames/frame_{}.jpg".format(i))
|
52 |
+
self.out.write(frame)
|
53 |
+
os.remove("video/frames/frame_{}.jpg".format(i))
|
54 |
+
self.out.release()
|
55 |
+
self.frame_count = 0
|
56 |
+
|
57 |
+
def gen_frames(self):
|
58 |
+
i = 0
|
59 |
+
if not self.cap or not self.cap.isOpened():
|
60 |
+
yield b'' # Return empty bytes if camera is not available
|
61 |
+
return
|
62 |
+
while True:
|
63 |
+
success, frame = self.cap.read()
|
64 |
+
if not success:
|
65 |
+
break
|
66 |
+
else:
|
67 |
+
if i % 20 == 0:
|
68 |
+
self.save_frame(frame)
|
69 |
+
ret, buffer = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 100])
|
70 |
+
frame = buffer.tobytes()
|
71 |
+
yield (b'--frame\r\n'
|
72 |
+
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
|
73 |
+
i += 1
|
74 |
+
if time.time() - self.start_time > self.save_duration * 3600:
|
75 |
+
self.create_video_from_frames()
|
76 |
+
self.start_time = time.time()
|
77 |
+
|
78 |
+
def close_camera(self):
|
79 |
+
try:
|
80 |
+
self.cap.release()
|
81 |
+
except: print("No camera to release")
|
82 |
+
try:
|
83 |
+
self.out.release()
|
84 |
+
except: print("No video to release")
|
85 |
+
|
86 |
+
|
87 |
+
class Sensor:
|
88 |
+
def __init__(self, id, name):
|
89 |
+
self.id = id
|
90 |
+
self.name = name
|
91 |
+
self.time = datetime.now()
|
92 |
+
self.measurement = ["Spotřeba","Napětí","Výkon","Teplota","Vlhkost","Vlhkost"]
|
93 |
+
self.values = [12,None,50,None,None,None] # store sensor values in a dictionary
|
94 |
+
self.units = ["A","V","D",None,None,None] # store sensor units in a dictionary
|
95 |
+
|
96 |
+
def update_values(self, data, units):
|
97 |
+
self.values = [round(float(d), 4) for d in data]
|
98 |
+
self.units = units
|
99 |
+
self.time = datetime.now()
|
100 |
+
print(self.values, self.units)
|
101 |
+
|
102 |
+
def find(devices, id):
|
103 |
+
for device in devices:
|
104 |
+
if device.id == id:
|
105 |
+
return device
|
106 |
+
return None # device not found
|
107 |
+
|
108 |
+
def plot(self, Db_class, title = "graph"):
|
109 |
+
# Get the current date and time
|
110 |
+
now = datetime.now()
|
111 |
+
|
112 |
+
# Calculate the date and time 10 days ago
|
113 |
+
ten_days_ago = now - timedelta(days=300)
|
114 |
+
|
115 |
+
# Query the database for records from the last 10 days
|
116 |
+
|
117 |
+
ele_1 = Db_class.query.filter(Db_class.sensor_id == self.id, Db_class.time >= ten_days_ago).all()
|
118 |
+
|
119 |
+
x = [record.time for record in ele_1]
|
120 |
+
y1 = [record.value4 for record in ele_1]
|
121 |
+
y2 = [record.value3 for record in ele_1] # Assuming value2 exists
|
122 |
+
|
123 |
+
fig = make_subplots(specs=[[{"secondary_y": True}]])
|
124 |
+
|
125 |
+
# Add traces
|
126 |
+
fig.add_trace(
|
127 |
+
go.Scatter(x=x, y=y1, name="Spotřeba['kWh'])"),
|
128 |
+
secondary_y=False,
|
129 |
+
)
|
130 |
+
|
131 |
+
fig.add_trace(
|
132 |
+
go.Scatter(x=x, y=y2, name="Aktuální výkon['W']"),
|
133 |
+
secondary_y=True,
|
134 |
+
)
|
135 |
+
|
136 |
+
fig.update_layout(
|
137 |
+
autosize=True,
|
138 |
+
title=title,
|
139 |
+
width=1200,
|
140 |
+
height=512,
|
141 |
+
margin=dict(
|
142 |
+
l=40,
|
143 |
+
r=20,
|
144 |
+
b=20,
|
145 |
+
t=60,
|
146 |
+
pad=2
|
147 |
+
),
|
148 |
+
paper_bgcolor='#F5F5F5',
|
149 |
+
plot_bgcolor='#FFFFFF'
|
150 |
+
)
|
151 |
+
|
152 |
+
fig.update_xaxes(title_text='Datum a čas')
|
153 |
+
fig.update_yaxes(title_text="Spotřeba [kWh]", secondary_y=False)
|
154 |
+
fig.update_yaxes(title_text="Aktuální výkon [W]", secondary_y=True)
|
155 |
+
|
156 |
+
return fig
|
device_routes.py
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from device_core import Camera, Sensor
|
2 |
+
|
3 |
+
# Define devices
|
4 |
+
SENSORS = [
|
5 |
+
Sensor("e1", "elektro spodní byt"),
|
6 |
+
Sensor("e2", "elektro horní byt"),
|
7 |
+
Sensor("e3", "elektro dílna"),
|
8 |
+
Sensor("e4", "elektro čerpadlo"),
|
9 |
+
Sensor("t1", "teplota spodní kuchyň"),
|
10 |
+
Sensor("t2", "teplota horní kuchyň"),
|
11 |
+
Sensor("t3", "teplota půda"),
|
12 |
+
Sensor("t4", "teplota venkovní"),
|
13 |
+
]
|
14 |
+
|
15 |
+
CAMERAS = [
|
16 |
+
#Camera(id="c1", name="Garáže", rtsp='rtsp://169.254.0.99:554/live.sdp'),
|
17 |
+
Camera(id="c2", name="Kůlna", index=0)
|
18 |
+
]
|
19 |
+
|
20 |
+
LIGHTING_DATA = {}
|
models.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask_sqlalchemy import SQLAlchemy
|
2 |
+
from datetime import datetime
|
3 |
+
|
4 |
+
db = SQLAlchemy()
|
5 |
+
|
6 |
+
class Device(db.Model):
|
7 |
+
id = db.Column(db.Integer, primary_key=True)
|
8 |
+
time = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
|
9 |
+
sensor_id = db.Column(db.String(64), nullable=False)
|
10 |
+
name = db.Column(db.String(50), nullable=False)
|
11 |
+
value1 = db.Column(db.Float, nullable=False)
|
12 |
+
value2 = db.Column(db.Float)
|
13 |
+
value3 = db.Column(db.Float)
|
14 |
+
value4 = db.Column(db.Float)
|
15 |
+
value5 = db.Column(db.Float)
|
16 |
+
value6 = db.Column(db.Float)
|
17 |
+
unit1 = db.Column(db.String(10))
|
18 |
+
unit2 = db.Column(db.String(10))
|
19 |
+
unit3 = db.Column(db.String(10))
|
20 |
+
unit4 = db.Column(db.String(10))
|
21 |
+
unit5 = db.Column(db.String(10))
|
22 |
+
unit6 = db.Column(db.String(10))
|
23 |
+
|
24 |
+
def init_db(app):
|
25 |
+
db.init_app(app)
|
26 |
+
with app.app_context():
|
27 |
+
db.create_all()
|
requirements.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
pip install Flask
|
2 |
+
pip install Flask-SQLAlchemy
|
3 |
+
pip install Flask-Bootstrap
|
4 |
+
pip install plotly
|
5 |
+
pip install requests
|
6 |
+
pip install opencv-python
|