WaterLevelSensor/Main.qml

614 lines
23 KiB
QML
Raw Normal View History

2026-04-01 18:33:19 +05:30
import QtQuick
import waterLevelSensor
import QtQuick.Effects
import QtQuick.Controls
import QtQuick.Controls.Material
Window {
width: 640
2026-04-04 12:24:03 +05:30
height: 1080
2026-04-01 18:33:19 +05:30
visible: true
title: qsTr("Hello World")
id: root
property color accent_color: "#F05FAF5F"
property color danger_color: "#F0AF2F1F"
property color water_color: "#F00F7FAF"
Material.accent: root.accent_color
property int font_enlarged_text_size: 20
2026-04-03 18:46:34 +05:30
property int font_normal_text_size: 16
2026-04-01 18:33:19 +05:30
property double distance: 0
property int distance_count: 0
// visibility: Window.FullScreen
2026-04-04 12:24:03 +05:30
Component.onCompleted: {
if(Qt.platform.os !== "android") {
}
}
2026-04-01 18:33:19 +05:30
Rectangle {
anchors.fill: parent
id: background
color: "#DF0F0F0F"
Rectangle {
id: top_bar
height: Math.min(root.height / 12, 100)
width: root.width
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
topMargin: 25
}
color: "#0A0F0F0F"
Rectangle {
id: settings_rect
height: top_bar.height - 20
width: top_bar.width / 12
anchors {
top: parent.top
left: parent.left
bottomMargin: 5
topMargin: 5
leftMargin: 5
}
Behavior on color {
ColorAnimation {
duration: 200
}
}
color: "#0A0F0F0F"
border.color: "#AF121212"
border.width: 0.5
radius: 2
Text {
id: settings_text
text: "⚙"
anchors.centerIn: parent
color: root.accent_color
font.pointSize: root.font_enlarged_text_size
}
MouseArea {
id: settings_button_area
anchors.fill: parent
hoverEnabled: true
property color settings_rect_pressed_prev: "#00000000"
property color settings_rect_entered_prev: "#00000000"
onEntered: {
settings_rect_entered_prev = settings_rect.color
settings_rect.color = "#5A1F1F1F"
}
onExited: {
settings_rect.color = settings_rect_entered_prev
}
onPressed: {
settings_rect_pressed_prev = settings_rect.color
settings_rect.color = "#5A1F4F1F"
settings_popup.open()
}
onReleased: {
settings_rect.color = settings_rect_pressed_prev
}
}
}
Rectangle {
property color old_color: "#0F0F0F0F"
id: status_indicator
width: 20
height: 20
radius: 20
anchors {
top: parent.top
right: parent.right
topMargin: 20
bottomMargin: 5
leftMargin: 5
rightMargin: 20
}
color: "#F0AF2F1F"
layer.enabled: true
layer.effect: MultiEffect {
shadowEnabled: true
}
}
Text {
id: status_label
anchors {
top: parent.top
topMargin: 20
right: status_indicator.left
rightMargin: 10
}
horizontalAlignment: Text.AlignRight
width: parent.width - status_indicator.width
height: parent.height
color: root.accent_color
font.pointSize: root.font_normal_text_size
text: "Disconnected"
}
}
Rectangle {
id: base
color: "#0A0F0F0F"
width: parent.width
height: parent.height - top_bar.height
anchors {
top: top_bar.bottom
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
Popup {
id: settings_popup
background: Rectangle {
color: "#F0121212"
radius: 5
border.color: root.accent_color
border.width: 1
}
height: parent.height / 1.5
width: parent.width / 1.5
anchors.centerIn: Overlay.overlay
Rectangle {
id: settings_base
anchors.fill: parent
color: "#F0121212"
Text {
id: settings_label
text: "Settings"
width: parent.width
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
}
horizontalAlignment: Text.AlignHCenter
color: root.accent_color
font.pointSize: root.font_enlarged_text_size
}
Text {
id: max_thres_label
text: "Max Threshold: "
anchors {
topMargin: 20
top: settings_label.bottom
left: parent.left
2026-04-04 12:24:03 +05:30
verticalCenter: max_thres_input.verticalCenter
2026-04-01 18:33:19 +05:30
}
height: max_thres_input.height
verticalAlignment: Text.AlignVCenter
color: root.accent_color
2026-04-03 18:46:34 +05:30
font.pointSize: root.font_normal_text_size
2026-04-01 18:33:19 +05:30
}
TextField {
id: max_thres_input
text: config.max
anchors {
2026-04-04 12:24:03 +05:30
right: parent.right
rightMargin: 5
2026-04-01 18:33:19 +05:30
top: settings_label.bottom
topMargin: 20
}
2026-04-03 18:46:34 +05:30
width: parent.width / 2
2026-04-01 18:33:19 +05:30
color: root.accent_color
2026-04-03 18:46:34 +05:30
font.pointSize: root.font_normal_text_size
2026-04-01 18:33:19 +05:30
}
Text {
id: min_thres_label
text: "Min Threshold: "
anchors {
topMargin: 20
top: max_thres_label.bottom
left: parent.left
2026-04-04 12:24:03 +05:30
verticalCenter: min_thres_input.verticalCenter
2026-04-01 18:33:19 +05:30
}
height: min_thres_input.height
verticalAlignment: Text.AlignVCenter
color: root.accent_color
2026-04-03 18:46:34 +05:30
font.pointSize: root.font_normal_text_size
2026-04-01 18:33:19 +05:30
}
TextField {
id: min_thres_input
text: config.min
anchors {
2026-04-04 12:24:03 +05:30
right: parent.right
rightMargin: 5
2026-04-01 18:33:19 +05:30
top: max_thres_label.bottom
topMargin: 20
}
2026-04-03 18:46:34 +05:30
width: parent.width / 2
2026-04-01 18:33:19 +05:30
color: root.accent_color
2026-04-03 18:46:34 +05:30
font.pointSize: root.font_normal_text_size
2026-04-01 18:33:19 +05:30
}
Text {
id: alert_thres_label
text: "Alert Percentage: "
anchors {
topMargin: 20
top: min_thres_label.bottom
left: parent.left
2026-04-04 12:24:03 +05:30
verticalCenter: alert_thres_input.verticalCenter
2026-04-01 18:33:19 +05:30
}
height: alert_thres_input.height
verticalAlignment: Text.AlignVCenter
color: root.accent_color
2026-04-03 18:46:34 +05:30
font.pointSize: root.font_normal_text_size
2026-04-01 18:33:19 +05:30
}
TextField {
id: alert_thres_input
text: config.alert
anchors {
2026-04-04 12:24:03 +05:30
right: parent.right
rightMargin: 5
2026-04-01 18:33:19 +05:30
top: min_thres_label.bottom
topMargin: 20
}
2026-04-03 18:46:34 +05:30
width: parent.width / 2
2026-04-01 18:33:19 +05:30
color: root.accent_color
2026-04-03 18:46:34 +05:30
font.pointSize: root.font_normal_text_size
2026-04-01 18:33:19 +05:30
}
2026-04-03 18:46:34 +05:30
CheckBox {
id: flip_bits_box
text: "Flip Bits"
checked: config.flipBits
anchors {
topMargin: 20
top: alert_thres_label.bottom
2026-04-04 12:24:03 +05:30
horizontalCenter: parent.horizontalCenter
2026-04-03 18:46:34 +05:30
}
contentItem: Text {
text: flip_bits_box.text
font.pointSize: root.font_normal_text_size
color: root.accent_color
leftPadding: flip_bits_box.indicator.width + flip_bits_box.spacing
}
}
2026-04-01 18:33:19 +05:30
Text {
id: settings_error_label
property string settings_error_text: ""
text: "Error: " + settings_error_text
visible: settings_error_text === "" ? false : true
anchors {
topMargin: 20
2026-04-03 18:46:34 +05:30
top: flip_bits_box.bottom
bottom: flip_bits_box.top
bottomMargin: 10
2026-04-01 18:33:19 +05:30
left: parent.left
horizontalCenter: parent.horizontalCenter
}
horizontalAlignment: Text.AlignHCenter
color: root.danger_color
font.pixelSize: root.font_normal_text_size
}
2026-04-03 18:46:34 +05:30
Rectangle {
id: reset_averages
anchors {
topMargin: 10
bottom: save_config.top
2026-04-04 12:24:03 +05:30
bottomMargin: 10
2026-04-03 18:46:34 +05:30
horizontalCenter: parent.horizontalCenter
}
color: "#F0121212"
2026-04-04 12:24:03 +05:30
width: parent.width / 1.3
2026-04-03 18:46:34 +05:30
height: 60
border {
width: 1
color: root.accent_color
}
radius: 3
2026-04-01 18:33:19 +05:30
2026-04-03 18:46:34 +05:30
Text {
anchors.centerIn: parent
text: "Reset Averages"
color: root.accent_color
font.pointSize: root.font_enlarged_text_size
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
property color settings_rect_pressed_prev: "#00000000"
property color settings_rect_entered_prev: "#00000000"
onEntered: {
2026-04-04 12:24:03 +05:30
settings_rect_entered_prev = reset_averages.color
reset_averages.color = "#5A1F1F1F"
2026-04-03 18:46:34 +05:30
}
onExited: {
2026-04-04 12:24:03 +05:30
reset_averages.color = settings_rect_entered_prev
2026-04-03 18:46:34 +05:30
}
onPressed: {
2026-04-04 12:24:03 +05:30
settings_rect_pressed_prev = reset_averages.color
reset_averages.color = "#5A1F4F1F"
2026-04-03 18:46:34 +05:30
listener.resetAverages();
}
onReleased: {
2026-04-04 12:24:03 +05:30
reset_averages.color = settings_rect_pressed_prev
2026-04-03 18:46:34 +05:30
}
}
}
2026-04-01 18:33:19 +05:30
Rectangle {
id: save_config
anchors {
topMargin: 10
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
color: "#F0121212"
2026-04-04 12:24:03 +05:30
width: parent.width / 1.3
2026-04-01 18:33:19 +05:30
height: 60
border {
width: 1
color: root.accent_color
}
radius: 3
Text {
anchors.centerIn: parent
text: "Save"
color: root.accent_color
font.pointSize: root.font_enlarged_text_size
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
property color settings_rect_pressed_prev: "#00000000"
property color settings_rect_entered_prev: "#00000000"
onEntered: {
settings_rect_entered_prev = save_config.color
save_config.color = "#5A1F1F1F"
}
onExited: {
save_config.color = settings_rect_entered_prev
}
onPressed: {
settings_rect_pressed_prev = save_config.color
save_config.color = "#5A1F4F1F"
let max = parseFloat(min_thres_input.text);
let min = parseFloat(min_thres_input.text);
let alert = parseFloat(alert_thres_input.text);
2026-04-03 18:46:34 +05:30
let flipBits = flip_bits_box.checked;
console.log("Parsed values:", max, min, alert, flip_bits_box.checked);
2026-04-01 18:33:19 +05:30
if(max < min) {
settings_error_label.settings_error_text = "Max cannot be less than min";
return;
}
if (alert > 100 || alert < 0) {
settings_error_label.settings_error_text = "Alert must be between 0 to 100";
return;
}
let current_min_reading = Math.max(0, (parseFloat(min_thres_input.text)));
let effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading);
let alert_effective_reading = (effective_max_reading * (100 - alert) / 100) + current_min_reading;
console.log("Alert effective reading is: " + alert_effective_reading + " for value: " + alert);
2026-04-03 18:46:34 +05:30
listener.setConfig(alert_effective_reading, flipBits);
config.saveConfig(max_thres_input.text, min_thres_input.text, alert_thres_input.text, flip_bits_box.checked);
2026-04-01 18:33:19 +05:30
settings_popup.close();
}
onReleased: {
save_config.color = settings_rect_pressed_prev
}
}
2026-04-03 18:46:34 +05:30
2026-04-01 18:33:19 +05:30
}
}
}
Rectangle {
id: tank_lid
color: "#F0121212"
anchors {
bottom: tank_base.top
horizontalCenter: tank_base.horizontalCenter
bottomMargin: -5
}
radius: 10
width: parent.width / 4
height: parent.height / 15
}
Rectangle {
id: tank_base
color: "#0FFFFFFF"
anchors.centerIn: parent
2026-04-04 12:24:03 +05:30
width: parent.width / 2
2026-04-01 18:33:19 +05:30
height: parent.height / 2
border.color: "#F0121212"
border.width: 5
radius: 4
}
Rectangle {
id: water_level
color: root.water_color
property double height_max: tank_base.height - 10
property double current_level: 0
property double percentage_filled: 0
height: water_level.current_level
width: tank_base.width - 10
anchors {
bottom: tank_base.bottom
horizontalCenter: tank_base.horizontalCenter
bottomMargin: 5
}
Behavior on color {
ColorAnimation {
duration: 200
}
}
Behavior on height {
NumberAnimation {
duration: 200
}
}
}
Text {
id: info_text
text: "Current Reading: " + root.distance.toFixed(2) + " cm"
anchors {
top: tank_base.bottom
topMargin: 20
horizontalCenter: parent.horizontalCenter
}
horizontalAlignment: Text.AlignHCenter
color: root.accent_color
font.pixelSize: root.font_normal_text_size
}
Text {
id: info_percent_text
text: "Percentage filled: " + water_level.percentage_filled.toFixed(2) + " %"
anchors {
top: info_text.bottom
topMargin: 10
horizontalCenter: parent.horizontalCenter
}
horizontalAlignment: Text.AlignHCenter
color: root.accent_color
font.pixelSize: root.font_normal_text_size
}
Text {
id: error_text
property string error_string: ""
text: "Error: " + error_string
visible: error_string === "" ? false : true;
anchors {
top: info_percent_text.bottom
topMargin: 10
horizontalCenter: parent.horizontalCenter
}
horizontalAlignment: Text.AlignHCenter
color: root.danger_color
font.pixelSize: root.font_normal_text_size
}
}
}
Timer {
id: status_indicator_timer
running: false
repeat: false
triggeredOnStart: false
interval: 300
onTriggered: {
status_indicator.color = status_indicator.old_color;
}
}
ConfigInterface {
id: config
onConfigData: (max, min, alert) => {
console.log("Got config data as: ", max, min, alert);
}
}
UDPListener {
id: listener
onShowError: (err) => {
error_text.error_string = err;
}
onDistanceChanged: {
console.log("Distance changed");
root.distance = listener.distance;
error_text.error_string = "";
console.log("Distance count: ", root.distance_count);
if(root.distance_count >= 5) {
status_label.text = "Recieving values";
let current_min_reading = Math.max(0, (parseFloat(min_thres_input.text)));
let effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading);
let current_reading = Math.max(0, root.distance - current_min_reading);
let height_max_possible = water_level.height_max;
let percentage_filled = Math.max(0, ((effective_max_reading - current_reading) / effective_max_reading * 100))
let normalized_height = percentage_filled * height_max_possible / 100;
let alert = parseFloat(alert_thres_input.text);
if(percentage_filled <= alert) {
console.log("Water Level is lower than alert");
water_level.color = root.danger_color;
}
else {
water_level.color = root.water_color;
}
2026-04-09 14:26:21 +05:30
if(root.distance_count > 1000) {
root.distance_count = 5;
}
2026-04-01 18:33:19 +05:30
console.log("Current reading is: " + current_reading)
console.log("Normalized height is: " + normalized_height)
water_level.current_level = normalized_height;
water_level.percentage_filled = percentage_filled;
}
}
onConnected: {
error_text.error_string = "";
console.log("Connected");
status_indicator.color = "#F05F5F0F"
status_label.text = "Waiting for more values";
}
onDisconnected: {
error_text.error_string = "Disconnected";
console.log("Disconnected");
status_indicator.color = "#F05F5F0F"
status_label.text = "Disconnected";
}
onDistanceReceived: (distance, time) => {
if(root.distance_count >= 1)
dataBlink();
if(root.distance_count == 0) {
let current_min_reading = Math.max(0, (parseFloat(min_thres_input.text)));
let effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading);
let alert = parseFloat(alert_thres_input.text);
let alert_effective_reading = (effective_max_reading * (100 - alert) / 100) + current_min_reading;
console.log("Alert effective reading is: " + alert_effective_reading + " for value: " + alert);
2026-04-03 18:46:34 +05:30
listener.setConfig(alert_effective_reading, flip_bits_box.checked);
2026-04-01 18:33:19 +05:30
}
distance_count += 1;
}
}
onClosing:(close)=> {
if(Qt.platform.os === "android"){
close.accepted = false
} else {
close.accepted = true
}
}
function dataBlink() {
2026-04-04 12:24:03 +05:30
if(status_indicator_timer.running) {
status_indicator_timer.stop();
}
let old_color = "#F05F5F0F";
2026-04-01 18:33:19 +05:30
let new_color = "#F04FAF5F";
status_indicator.color = new_color;
status_indicator.old_color = old_color;
status_indicator_timer.start();
}
}