import QtQuick import waterLevelSensor import QtQuick.Effects import QtQuick.Controls import QtQuick.Controls.Material Window { width: 640 height: 1080 visible: true title: qsTr("Hello World") id: root property color accent_color: "#F05FAF5F" property color dark_accent_color: "#FF5F7F5F" property color danger_color: "#F0AF2F1F" property color water_color: "#F00F7FAF" Material.accent: root.accent_color property int font_enlarged_text_size: 20 property int font_normal_text_size: 16 property double distance: 0 property int distance_count: 0 property string mode: "http" // visibility: Window.FullScreen Component.onCompleted: { if(Qt.platform.os !== "android") { } } 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: Math.max(parent.height / 1.2, contentHeight) width: parent.width / 1.1 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 verticalCenter: max_thres_input.verticalCenter } height: max_thres_input.height verticalAlignment: Text.AlignVCenter color: root.accent_color font.pointSize: root.font_normal_text_size } TextField { id: max_thres_input text: config.max anchors { right: parent.right rightMargin: 5 top: settings_label.bottom topMargin: 20 } width: parent.width / 2 color: root.accent_color font.pointSize: root.font_normal_text_size } Text { id: min_thres_label text: "Min Threshold: " anchors { topMargin: 20 top: max_thres_label.bottom left: parent.left verticalCenter: min_thres_input.verticalCenter } height: min_thres_input.height verticalAlignment: Text.AlignVCenter color: root.accent_color font.pointSize: root.font_normal_text_size } TextField { id: min_thres_input text: config.min anchors { right: parent.right rightMargin: 5 top: max_thres_label.bottom topMargin: 20 } width: parent.width / 2 color: root.accent_color font.pointSize: root.font_normal_text_size } Text { id: alert_thres_label text: "Alert Percentage: " anchors { topMargin: 20 top: min_thres_label.bottom left: parent.left verticalCenter: alert_thres_input.verticalCenter } height: alert_thres_input.height verticalAlignment: Text.AlignVCenter color: root.accent_color font.pointSize: root.font_normal_text_size } TextField { id: alert_thres_input text: config.alert anchors { right: parent.right rightMargin: 5 top: min_thres_label.bottom topMargin: 20 } width: parent.width / 2 color: root.accent_color font.pointSize: root.font_normal_text_size } CheckBox { id: flip_bits_box text: "Flip Bits" checked: config.flipBits anchors { topMargin: 20 top: alert_thres_label.bottom horizontalCenter: parent.horizontalCenter } 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 } } Text { id: settings_mode_text anchors { topMargin: 20 top: flip_bits_box.bottom left: parent.left } font.pointSize: root.font_normal_text_size color: root.accent_color text: "Mode: " } Row { id: mode_row anchors { left: settings_mode_text.right right: parent.right verticalCenter: settings_mode_text.verticalCenter leftMargin: 10 } RadioButton { id: udp_radio_button checked: config.mode === "udp" ? true : false text: "UDP" font.pointSize: root.font_normal_text_size contentItem: Text { text: udp_radio_button.text font: udp_radio_button.font opacity: enabled ? 1.0 : 0.3 color: udp_radio_button.down ? root.dark_accent_color : root.accent_color verticalAlignment: Text.AlignVCenter leftPadding: udp_radio_button.indicator.width + udp_radio_button.spacing } onCheckedChanged: { if(checked) { root.mode = "udp"; } } } RadioButton { id: http_radio_button checked: config.mode === "http" ? true : false text: "HTTP" font.pointSize: root.font_normal_text_size contentItem: Text { text: http_radio_button.text font: http_radio_button.font opacity: enabled ? 1.0 : 0.3 color: http_radio_button.down ? root.dark_accent_color : root.accent_color verticalAlignment: Text.AlignVCenter leftPadding: http_radio_button.indicator.width + http_radio_button.spacing } onCheckedChanged: { if(checked) { root.mode = "http"; } } } } Text { id: http_url_label text: "Url: " anchors { topMargin: 20 top: settings_mode_text.bottom left: parent.left verticalCenter: http_url_input.verticalCenter } height: http_url_input.height verticalAlignment: Text.AlignVCenter color: root.accent_color font.pointSize: root.font_normal_text_size } TextField { id: http_url_input text: config.url anchors { right: parent.right rightMargin: 20 leftMargin: 20 top: mode_row.bottom topMargin: 20 } enabled: root.mode === "http" ? true : false width: Math.max(parent.width / 2, parent.width - http_url_label.width - anchors.leftMargin) color: root.accent_color font.pointSize: root.font_normal_text_size cursorPosition: 0 } Text { id: settings_error_label property string settings_error_text: "" text: "Error: " + settings_error_text visible: settings_error_text === "" ? false : true anchors { topMargin: 20 top: http_url_label.bottom bottom: reset_averages.top bottomMargin: 10 left: parent.left horizontalCenter: parent.horizontalCenter } horizontalAlignment: Text.AlignHCenter color: root.danger_color font.pixelSize: root.font_normal_text_size } Rectangle { id: reset_averages anchors { topMargin: 10 bottom: save_config.top bottomMargin: 10 horizontalCenter: parent.horizontalCenter } color: "#F0121212" width: parent.width / 1.3 height: 60 border { width: 1 color: root.accent_color } radius: 3 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: { settings_rect_entered_prev = reset_averages.color reset_averages.color = "#5A1F1F1F" } onExited: { reset_averages.color = settings_rect_entered_prev } onPressed: { settings_rect_pressed_prev = reset_averages.color reset_averages.color = "#5A1F4F1F" listener.resetAverages(); } onReleased: { reset_averages.color = settings_rect_pressed_prev } } } Rectangle { id: save_config anchors { topMargin: 10 bottom: parent.bottom horizontalCenter: parent.horizontalCenter } color: "#F0121212" width: parent.width / 1.3 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); let flipBits = flip_bits_box.checked; let mode = root.mode; let url = http_url_input.text; console.log("Parsed values:", max, min, alert, flip_bits_box.checked); 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); listener.setConfig(alert_effective_reading, flipBits, mode, url); config.saveConfig(max_thres_input.text, min_thres_input.text, alert_thres_input.text, flip_bits_box.checked, mode, url); settings_popup.close(); } onReleased: { save_config.color = settings_rect_pressed_prev } } } } } 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 width: parent.width / 2 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: last_updated_text property string last_updated: "" text: "Last Updated On Server On: " + last_updated anchors { top: info_percent_text.bottom topMargin: 10 horizontalCenter: parent.horizontalCenter } horizontalAlignment: Text.AlignHCenter color: root.accent_color font.pixelSize: root.font_normal_text_size visible: config.mode === "http" ? true : false } Text { id: error_text property string error_string: "" text: "Error: " + error_string visible: error_string === "" ? false : true; anchors { top: last_updated_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, mode, url) => { console.log("Got config data as: ", max, min, alert, mode, url); } onModeChanged: { root.mode = mode; } } Timer { property int count: 0 id: init_timer running: true repeat: true interval: 1000 onTriggered: { if(count === 0) { listener.startService(); count++; return; } else { console.log("Timer triggered saving config, hope everything is loaded by now"); 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("Setting initial configs: ", alert_effective_reading, config.flipBits , config.mode, config.url); listener.setConfig(alert_effective_reading, config.flipBits , config.mode, config.url); http_url_input.cursorPosition = 0; init_timer.repeat = false; init_timer.stop(); } } } UDPListener { id: listener onShowError: (err) => { error_text.error_string = err; } onLastUpdatedChanged: (update, warn) => { console.log("QML Last Updated: ", update); if(!warn) { last_updated_text.last_updated = update; last_updated_text.color = root.accent_color; } else { last_updated_text.last_updated = update; last_updated_text.color = root.danger_color; } } 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; } if(root.distance_count > 1000) { root.distance_count = 5; } 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); listener.setConfig(alert_effective_reading, flip_bits_box.checked, root.mode, http_url_input.text); } distance_count += 1; } } onClosing: (close)=> { if(Qt.platform.os === "android"){ // close.accepted = false // listener.moveAppToBackground(); // console.log("Moving to backgound"); close.accepted = true; } else { close.accepted = true } } function dataBlink() { if(status_indicator_timer.running) { status_indicator_timer.stop(); } let old_color = "#F05F5F0F"; let new_color = "#F04FAF5F"; status_indicator.color = new_color; status_indicator.old_color = old_color; status_indicator_timer.start(); } }