Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b05724f7b5 | |||
| 26b489ae5f | |||
| 8cb17f1880 | |||
| 314b57b9c4 | |||
| 93995959d0 | |||
| b8201398e1 | |||
| ca73f70b63 | |||
| 1d999f6c33 | |||
| 07bd73bb33 |
18 changed files with 1335 additions and 262 deletions
|
|
@ -3,11 +3,16 @@ cmake_minimum_required(VERSION 3.16)
|
||||||
project(WaterLevelRewrite VERSION 0.1 LANGUAGES CXX)
|
project(WaterLevelRewrite VERSION 0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Quick)
|
find_package(Qt6 REQUIRED COMPONENTS Gui Core Quick Multimedia)
|
||||||
|
|
||||||
qt_standard_project_setup(REQUIRES 6.10)
|
qt_standard_project_setup(REQUIRES 6.10)
|
||||||
|
|
||||||
|
if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/importedcontent/CMakeLists.txt")
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/importedcontent)
|
||||||
|
endif()
|
||||||
|
|
||||||
qt_add_executable(appWaterLevelRewrite
|
qt_add_executable(appWaterLevelRewrite
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
@ -16,27 +21,45 @@ qt_add_qml_module(appWaterLevelRewrite
|
||||||
URI WaterLevelRewrite
|
URI WaterLevelRewrite
|
||||||
QML_FILES
|
QML_FILES
|
||||||
Main.qml
|
Main.qml
|
||||||
QML_FILES WaterTank.qml
|
WaterTank.qml
|
||||||
QML_FILES SettingsBarIcon.qml
|
SettingsBarIcon.qml
|
||||||
QML_FILES SettingsPopup.qml
|
SettingsPopup.qml
|
||||||
SOURCES configinterface.h configinterface.cpp
|
ConnectButton.qml
|
||||||
QML_FILES ConnectButton.qml
|
SOURCES
|
||||||
SOURCES requestworker.h requestworker.cpp
|
configinterface.h
|
||||||
|
configinterface.cpp
|
||||||
|
requestworker.h
|
||||||
|
requestworker.cpp
|
||||||
|
wifihelper.h
|
||||||
|
wifihelper.cpp
|
||||||
|
RESOURCES
|
||||||
|
media.qrc
|
||||||
|
QML_FILES CustomCheckBox.qml
|
||||||
|
QML_FILES RequestResponsePopup.qml
|
||||||
|
QML_FILES ColorInput.qml
|
||||||
)
|
)
|
||||||
|
|
||||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
if(APPLE)
|
||||||
# If you are developing for iOS or macOS you should consider setting an
|
|
||||||
# explicit, fixed bundle identifier manually though.
|
|
||||||
set_target_properties(appWaterLevelRewrite PROPERTIES
|
set_target_properties(appWaterLevelRewrite PROPERTIES
|
||||||
# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appWaterLevelRewrite
|
# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appWaterLevelRewrite
|
||||||
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
||||||
MACOSX_BUNDLE TRUE
|
MACOSX_BUNDLE TRUE
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set_target_properties(appWaterLevelRewrite PROPERTIES
|
||||||
WIN32_EXECUTABLE TRUE
|
WIN32_EXECUTABLE TRUE
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(appWaterLevelRewrite PROPERTIES
|
||||||
|
QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android"
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(appWaterLevelRewrite
|
target_link_libraries(appWaterLevelRewrite
|
||||||
PRIVATE Qt6::Quick
|
PRIVATE Qt6::Quick Qt6::Multimedia Qt6::Gui Qt6::Core
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS appWaterLevelRewrite
|
install(TARGETS appWaterLevelRewrite
|
||||||
|
|
|
||||||
21
ColorInput.qml
Normal file
21
ColorInput.qml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: color_text
|
||||||
|
text: "F05FAF5F"
|
||||||
|
property alias color_text: base.color
|
||||||
|
Rectangle {
|
||||||
|
id: base
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 10
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
width: 10
|
||||||
|
height: parent.height / 2
|
||||||
|
color: "#" + color_text.text
|
||||||
|
}
|
||||||
|
}
|
||||||
17
CustomCheckBox.qml
Normal file
17
CustomCheckBox.qml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls.Basic
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: control
|
||||||
|
property color font_color: "#0000FF00"
|
||||||
|
property int font_point_size: 22
|
||||||
|
font.pointSize: font_point_size
|
||||||
|
checked: true
|
||||||
|
contentItem: Text {
|
||||||
|
text: control.text
|
||||||
|
font: control.font
|
||||||
|
color: control.font_color
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
leftPadding: control.indicator.width + control.spacing
|
||||||
|
}
|
||||||
|
}
|
||||||
253
Main.qml
253
Main.qml
|
|
@ -5,8 +5,8 @@ import QtQuick.Controls
|
||||||
import QtQuick.Controls.Material
|
import QtQuick.Controls.Material
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
width: 640
|
width: 960
|
||||||
height: 1080
|
height: 1400
|
||||||
visible: true
|
visible: true
|
||||||
title: qsTr("Water Level Rewrite")
|
title: qsTr("Water Level Rewrite")
|
||||||
id: root
|
id: root
|
||||||
|
|
@ -14,6 +14,7 @@ Window {
|
||||||
property color dark_accent_color: "#FF5F7F5F"
|
property color dark_accent_color: "#FF5F7F5F"
|
||||||
property color danger_color: "#F0AF2F1F"
|
property color danger_color: "#F0AF2F1F"
|
||||||
property color water_color: "#F00F7FAF"
|
property color water_color: "#F00F7FAF"
|
||||||
|
property color water_color_above_alert: "#F00FAF0F"
|
||||||
property color background_color: "#1F1F1F"
|
property color background_color: "#1F1F1F"
|
||||||
Material.accent: root.accent_color
|
Material.accent: root.accent_color
|
||||||
property int font_enlarged_text_size: 20
|
property int font_enlarged_text_size: 20
|
||||||
|
|
@ -21,12 +22,38 @@ Window {
|
||||||
property int distance_count_thres: 3
|
property int distance_count_thres: 3
|
||||||
property string mode: "http"
|
property string mode: "http"
|
||||||
property string status: "Initializing"
|
property string status: "Initializing"
|
||||||
|
property string connection_text_string: "Reading WiFi Status..."
|
||||||
|
property string alarm_status_text: ""
|
||||||
|
property double water_tank_height_factor: 1.3
|
||||||
|
property double water_tank_width_factor: 2
|
||||||
|
property var root_config: ({})
|
||||||
|
property var base_config: {
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"alert": 0,
|
||||||
|
"interval": 0,
|
||||||
|
"url": "none",
|
||||||
|
"water_tank_width": 2,
|
||||||
|
"water_tank_height": 1.3,
|
||||||
|
"rate_of_fill_enable": true,
|
||||||
|
"current_reading_enable": true,
|
||||||
|
"percentage_enable": true,
|
||||||
|
"filling_status_enable": true,
|
||||||
|
"expected_to_fill_enable": true,
|
||||||
|
"accent_color": "F05FAF5F",
|
||||||
|
"dark_accent_color": "FF5F7F5F",
|
||||||
|
"danger_color": "F0AF2F1F",
|
||||||
|
"water_color": "F00F7FAF",
|
||||||
|
"water_color_above_alert": "F00FAF0F",
|
||||||
|
"background_color": "1F1F1F",
|
||||||
|
"font_normal_text_size": 16,
|
||||||
|
"font_enlarged_text_size": 20
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: root.background_color
|
color: root.background_color
|
||||||
|
id: root_rect
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: top_bar
|
id: top_bar
|
||||||
height: Math.min(root.height / 12, 100)
|
height: Math.min(root.height / 12, 100)
|
||||||
|
|
@ -38,6 +65,7 @@ Window {
|
||||||
}
|
}
|
||||||
color: "#0A0F0F0F"
|
color: "#0A0F0F0F"
|
||||||
SettingsBarIcon {
|
SettingsBarIcon {
|
||||||
|
id: settings_icon
|
||||||
height: top_bar.height - 20
|
height: top_bar.height - 20
|
||||||
width: height
|
width: height
|
||||||
anchors {
|
anchors {
|
||||||
|
|
@ -54,12 +82,40 @@ Window {
|
||||||
settings_popup.open()
|
settings_popup.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SettingsBarIcon {
|
||||||
|
height: top_bar.height - 20
|
||||||
|
width: height
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
bottomMargin: 5
|
||||||
|
topMargin: 10
|
||||||
|
leftMargin: 5
|
||||||
|
}
|
||||||
|
text: "⇅"
|
||||||
|
color: "#0A0F0F0F"
|
||||||
|
radius: 100
|
||||||
|
accent_color: root.accent_color
|
||||||
|
onOpen: {
|
||||||
|
request_response_popup.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestResponsePopup {
|
||||||
|
id: request_response_popup
|
||||||
|
accent_color: root.accent_color
|
||||||
|
font_enlarged_text_size: 20
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsPopup {
|
SettingsPopup {
|
||||||
id: settings_popup
|
id: settings_popup
|
||||||
accent_color: root.accent_color
|
accent_color: root.accent_color
|
||||||
font_enlarged_text_size: 20
|
background_color: root.background_color
|
||||||
|
font_enlarged_text_size: root.font_enlarged_text_size
|
||||||
|
font_normal_text_size: root.font_normal_text_size
|
||||||
function setConfig(new_config: var) {
|
function setConfig(new_config: var) {
|
||||||
settings_popup.config.max = new_config.max
|
settings_popup.config.max = new_config.max
|
||||||
settings_popup.config.min = new_config.min
|
settings_popup.config.min = new_config.min
|
||||||
|
|
@ -70,32 +126,66 @@ Window {
|
||||||
console.log("Saving config: ", config)
|
console.log("Saving config: ", config)
|
||||||
config_interface.saveConfig(config);
|
config_interface.saveConfig(config);
|
||||||
}
|
}
|
||||||
|
onDeleteConfig: () => {
|
||||||
|
config_interface.deleteConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: connection_text
|
||||||
|
anchors {
|
||||||
|
top: top_bar.bottom
|
||||||
|
topMargin: 20
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
text: root.connection_text_string
|
||||||
|
color: root.accent_color
|
||||||
|
font.pointSize: root.font_normal_text_size
|
||||||
}
|
}
|
||||||
|
|
||||||
WaterTank {
|
WaterTank {
|
||||||
id: tank
|
id: tank
|
||||||
anchors {
|
anchors {
|
||||||
top: top_bar.bottom
|
top: connection_text.bottom
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
topMargin: 100
|
topMargin: 40
|
||||||
}
|
}
|
||||||
height: (parent.height - top_bar.height) / 2
|
height: (root_rect.height) / root.water_tank_height_factor
|
||||||
width: parent.width / 2.2
|
width: root_rect.width / root.water_tank_width_factor
|
||||||
base_color: root.background_color
|
base_color: root.background_color
|
||||||
distance: 0
|
distance: 0
|
||||||
tank_lid_color: "#F0121212"
|
tank_lid_color: "#F0121212"
|
||||||
tank_base_color: "#0FFFFFFF"
|
tank_base_color: "#0FFFFFFF"
|
||||||
water_level_color: "#F00F7FAF"
|
water_level_color: root.water_color
|
||||||
tank_base_border_color: "#F0121212"
|
tank_base_border_color: "#F0121212"
|
||||||
|
water_color_above_alert: root.water_color_above_alert
|
||||||
min_thres_input: 0
|
min_thres_input: 0
|
||||||
max_thres_input: 0
|
max_thres_input: 0
|
||||||
|
text_color: root.accent_color
|
||||||
|
font_normal_text_size: root.font_normal_text_size
|
||||||
|
font_enlarged_text_size: root.font_enlarged_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: alarm_status
|
||||||
|
text: root.alarm_status_text
|
||||||
|
visible: (root.alarm_status_text.length > 0 ) ? true : false
|
||||||
|
anchors {
|
||||||
|
bottom: status.top
|
||||||
|
bottomMargin: 10
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: root.accent_color
|
||||||
|
font.pointSize: root.font_normal_text_size
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: status
|
id: status
|
||||||
text: root.status
|
text: root.status
|
||||||
anchors {
|
anchors {
|
||||||
bottom: connect_button.top
|
bottom: button_base.top
|
||||||
bottomMargin: 20
|
bottomMargin: 20
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
|
|
@ -104,19 +194,30 @@ Window {
|
||||||
font.pointSize: root.font_normal_text_size
|
font.pointSize: root.font_normal_text_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: button_base
|
||||||
|
color: "#00000000"
|
||||||
|
width: parent.width / 1.5
|
||||||
|
height: parent.height / 20
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 30
|
||||||
|
}
|
||||||
ConnectButton {
|
ConnectButton {
|
||||||
id: connect_button
|
id: connect_button
|
||||||
height: parent.height / 20
|
width: parent.width / 2.1
|
||||||
width: parent.width / 4
|
height: parent.height
|
||||||
font_color: root.accent_color
|
font_color: root.accent_color
|
||||||
font_enlarged_text_size: root.font_enlarged_text_size
|
font_enlarged_text_size: root.font_enlarged_text_size
|
||||||
font_normal_text_size: root.font_normal_text_size
|
font_normal_text_size: root.font_normal_text_size
|
||||||
color: root.background_color
|
color: root.background_color
|
||||||
accent_color: root.accent_color
|
accent_color: root.accent_color
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
left: parent.left
|
||||||
|
leftMargin: 5
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
bottomMargin: 50
|
bottomMargin: 5
|
||||||
}
|
}
|
||||||
onConnect: {
|
onConnect: {
|
||||||
if(connect_button.mode === 0)
|
if(connect_button.mode === 0)
|
||||||
|
|
@ -130,6 +231,40 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConnectButton {
|
||||||
|
id: start_alarm
|
||||||
|
width: parent.width / 2.1
|
||||||
|
height: parent.height
|
||||||
|
font_color: root.accent_color
|
||||||
|
font_enlarged_text_size: root.font_enlarged_text_size
|
||||||
|
font_normal_text_size: root.font_normal_text_size
|
||||||
|
color: root.background_color
|
||||||
|
accent_color: root.accent_color
|
||||||
|
text: "Start Alarm"
|
||||||
|
text2: "Stop Alarm"
|
||||||
|
anchors {
|
||||||
|
left: connect_button.right
|
||||||
|
leftMargin: 10
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 5
|
||||||
|
}
|
||||||
|
onConnect: {
|
||||||
|
if(start_alarm.mode === 0)
|
||||||
|
{
|
||||||
|
requester.startAlarm();
|
||||||
|
root.alarm_status_text = "Started Alarm; Waiting for Trigger: " + root.root_config.alert
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
requester.stopAlarm();
|
||||||
|
root.alarm_status_text = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestWorker {
|
RequestWorker {
|
||||||
|
|
@ -137,14 +272,26 @@ Window {
|
||||||
onStatusChanged: () => {
|
onStatusChanged: () => {
|
||||||
root.status = requester.status;
|
root.status = requester.status;
|
||||||
}
|
}
|
||||||
onNetworkReply: (water_level, fill_rate, expected) => {
|
onNetworkReply: (water_level, fill_rate, expected, data) => {
|
||||||
tank.setDistance(water_level);
|
tank.setDistance(water_level);
|
||||||
tank.rate_of_fill = fill_rate;
|
tank.rate_of_fill = fill_rate;
|
||||||
tank.expected_to_fill = expected;
|
tank.expected_to_fill = expected;
|
||||||
status.color = root.accent_color;
|
status.color = root.accent_color;
|
||||||
|
request_response_popup.request_response_string = data;
|
||||||
}
|
}
|
||||||
onRequestFailed: () => {
|
onRequestFailed: (data) => {
|
||||||
status.color = root.danger_color;
|
status.color = root.danger_color;
|
||||||
|
request_response_popup.request_response_string = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
onConnectionText: (text, error) => {
|
||||||
|
root.connection_text_string = text
|
||||||
|
if(!error) {
|
||||||
|
connection_text.color = root.accent_color
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
connection_text.color = root.danger_color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,16 +299,86 @@ Window {
|
||||||
id: config_interface
|
id: config_interface
|
||||||
fileName: "config.json"
|
fileName: "config.json"
|
||||||
onConfig: (config) => {
|
onConfig: (config) => {
|
||||||
settings_popup.config = config
|
settings_popup.config = config;
|
||||||
console.log("Loading initial config");
|
console.log("Loading initial config");
|
||||||
setConfig(config);
|
setConfig(config);
|
||||||
if(root.status === "Initializing") {
|
if(root.status === "Initializing") {
|
||||||
root.status = "Press Connect.";
|
root.status = "Press Connect.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onConfigDeleted: () => {
|
||||||
|
let config = base_config;
|
||||||
|
settings_popup.config = config;
|
||||||
|
console.log("Loading initial config");
|
||||||
|
setConfig(config);
|
||||||
|
if(root.status === "Initializing") {
|
||||||
|
root.status = "Press Connect.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setConfig(config) {
|
function setConfig(config) {
|
||||||
tank.setConfig(config);
|
tank.setConfig(config);
|
||||||
requester.setConfig(config);
|
requester.setConfig(config);
|
||||||
|
root.root_config = config;
|
||||||
|
if(config.water_tank_height === undefined) {
|
||||||
|
config.water_tank_height = 2
|
||||||
|
}
|
||||||
|
if(config.water_tank_width === undefined) {
|
||||||
|
config.water_tank_width = 2.2
|
||||||
|
}
|
||||||
|
if(config.rate_of_fill_enable === undefined) {
|
||||||
|
config.rate_of_fill_enable = true
|
||||||
|
}
|
||||||
|
if(config.current_reading_enable === undefined) {
|
||||||
|
config.current_reading_enable = true
|
||||||
|
}
|
||||||
|
if(config.percentage_enable === undefined) {
|
||||||
|
config.percentage_enable = true
|
||||||
|
}
|
||||||
|
if(config.filling_status_enable === undefined) {
|
||||||
|
config.filling_status_enable = true
|
||||||
|
}
|
||||||
|
if(config.expected_to_fill_enable === undefined) {
|
||||||
|
config.expected_to_fill_enable = true
|
||||||
|
}
|
||||||
|
if(config.font_enlarged_text_size === undefined) {
|
||||||
|
config.font_enlarged_text_size = 20
|
||||||
|
}
|
||||||
|
if(config.font_normal_text_size === undefined) {
|
||||||
|
config.font_normal_text_size = 16
|
||||||
|
}
|
||||||
|
if(config.accent_color === undefined) {
|
||||||
|
config.accent_color = "F05FAF5F"
|
||||||
|
}
|
||||||
|
if(config.danger_color === undefined) {
|
||||||
|
config.danger_color = "F0AF2F1F"
|
||||||
|
}
|
||||||
|
if(config.dark_accent_color === undefined) {
|
||||||
|
config.dark_accent_color = "FF5F7F5F"
|
||||||
|
}
|
||||||
|
if(config.water_color === undefined) {
|
||||||
|
config.water_color = "F00F7FAF"
|
||||||
|
}
|
||||||
|
if(config.water_color_above_alert === undefined) {
|
||||||
|
config.water_color_above_alert = "F00FAF0F"
|
||||||
|
}
|
||||||
|
if(config.background_color === undefined) {
|
||||||
|
config.background_color = "1F1F1F"
|
||||||
|
}
|
||||||
|
root.water_tank_height_factor = config.water_tank_height
|
||||||
|
root.water_tank_width_factor = config.water_tank_width
|
||||||
|
root.accent_color = "#" + config.accent_color
|
||||||
|
root.danger_color = "#" + config.danger_color
|
||||||
|
root.dark_accent_color = "#" + config.dark_accent_color
|
||||||
|
root.water_color = "#" + config.water_color
|
||||||
|
root.water_color_above_alert = "#" + config.water_color_above_alert
|
||||||
|
root.background_color = "#" + config.background_color
|
||||||
|
root.font_enlarged_text_size = config.font_enlarged_text_size
|
||||||
|
root.font_normal_text_size = config.font_normal_text_size
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onClosing: {
|
||||||
|
requester.stopAlarm();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
RequestResponsePopup.qml
Normal file
30
RequestResponsePopup.qml
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: request_response_popup
|
||||||
|
property color accent_color: "#00000000"
|
||||||
|
property int font_enlarged_text_size: 20
|
||||||
|
property int font_normal_text_size: 16
|
||||||
|
property string request_response_string: ""
|
||||||
|
background: Rectangle {
|
||||||
|
color: "#F0121212"
|
||||||
|
radius: 5
|
||||||
|
border.color: request_response_popup.accent_color
|
||||||
|
border.width: 1
|
||||||
|
}
|
||||||
|
height: Math.max(parent.height / 1.6, contentHeight)
|
||||||
|
width: parent.width / 1.2
|
||||||
|
anchors.centerIn: Overlay.overlay
|
||||||
|
ScrollView {
|
||||||
|
id: view
|
||||||
|
anchors.fill: parent
|
||||||
|
TextArea {
|
||||||
|
text: request_response_string
|
||||||
|
font.pointSize: font_normal_text_size
|
||||||
|
color: request_response_popup.accent_color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ Rectangle {
|
||||||
signal open;
|
signal open;
|
||||||
property color accent_color: "#00000000"
|
property color accent_color: "#00000000"
|
||||||
property int font_enlarged_text_size: 20
|
property int font_enlarged_text_size: 20
|
||||||
|
property string text: "⚙"
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
duration: 200
|
duration: 200
|
||||||
|
|
@ -15,8 +16,10 @@ Rectangle {
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
id: settings_text
|
id: settings_text
|
||||||
text: "⚙"
|
text: settings_rect.text
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
color: settings_rect.accent_color
|
color: settings_rect.accent_color
|
||||||
font.pointSize: settings_rect.font_enlarged_text_size
|
font.pointSize: settings_rect.font_enlarged_text_size
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,32 @@ import QtQuick.Controls.Material
|
||||||
Popup {
|
Popup {
|
||||||
id: settings_popup
|
id: settings_popup
|
||||||
signal saveConfig(config: var)
|
signal saveConfig(config: var)
|
||||||
|
signal deleteConfig()
|
||||||
property color accent_color: "#00000000"
|
property color accent_color: "#00000000"
|
||||||
property int font_enlarged_text_size: 20
|
property int font_enlarged_text_size: 20
|
||||||
property int font_normal_text_size: 16
|
property int font_normal_text_size: 16
|
||||||
|
property color background_color: "#00000000"
|
||||||
property var config: {
|
property var config: {
|
||||||
"max": 0,
|
"max": 0,
|
||||||
"min": 0,
|
"min": 0,
|
||||||
"alert": 0,
|
"alert": 0,
|
||||||
"interval": 0,
|
"interval": 0,
|
||||||
"url": "none"
|
"url": "none",
|
||||||
|
"water_tank_width": 2,
|
||||||
|
"water_tank_height": 1.3,
|
||||||
|
"rate_of_fill_enable": true,
|
||||||
|
"current_reading_enable": true,
|
||||||
|
"percentage_enable": true,
|
||||||
|
"filling_status_enable": true,
|
||||||
|
"expected_to_fill_enable": true,
|
||||||
|
"accent_color": "F05FAF5F",
|
||||||
|
"dark_accent_color": "FF5F7F5F",
|
||||||
|
"danger_color": "F0AF2F1F",
|
||||||
|
"water_color": "F00F7FAF",
|
||||||
|
"water_color_above_alert": "F00FAF0F",
|
||||||
|
"background_color": "1F1F1F",
|
||||||
|
"font_normal_text_size": 16,
|
||||||
|
"font_enlarged_text_size": 20
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
|
|
@ -31,170 +48,461 @@ Popup {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "#F0121212"
|
color: "#F0121212"
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: rect_base_scroll_view
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
bottom: save_config.top
|
||||||
|
bottomMargin: 10
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
// Use contentWidth and contentHeight to define the scrollable area
|
||||||
|
contentWidth: availableWidth
|
||||||
|
contentHeight: settings_column.height
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: settings_column
|
||||||
|
width: rect_base_scroll_view.width
|
||||||
|
spacing: 20
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: settings_label
|
id: settings_label
|
||||||
text: "Settings"
|
text: "Settings"
|
||||||
width: parent.width
|
width: parent.width
|
||||||
anchors {
|
|
||||||
top: parent.top
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
color: settings_popup.accent_color
|
color: settings_popup.accent_color
|
||||||
font.pointSize: settings_popup.font_enlarged_text_size
|
font.pointSize: settings_popup.font_enlarged_text_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Max Threshold
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
Text {
|
Text {
|
||||||
id: max_thres_label
|
|
||||||
text: "Max Threshold: "
|
text: "Max Threshold: "
|
||||||
anchors {
|
width: parent.width * 0.4
|
||||||
topMargin: 20
|
|
||||||
top: settings_label.bottom
|
|
||||||
left: parent.left
|
|
||||||
verticalCenter: max_thres_input.verticalCenter
|
|
||||||
}
|
|
||||||
height: max_thres_input.height
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
color: settings_popup.accent_color
|
color: settings_popup.accent_color
|
||||||
font.pointSize: settings_popup.font_normal_text_size
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: max_thres_input.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: max_thres_input
|
id: max_thres_input
|
||||||
text: config.max
|
text: config.max
|
||||||
anchors {
|
width: parent.width * 0.55
|
||||||
right: parent.right
|
|
||||||
rightMargin: 5
|
|
||||||
top: settings_label.bottom
|
|
||||||
topMargin: 20
|
|
||||||
}
|
|
||||||
width: parent.width / 2
|
|
||||||
color: settings_popup.accent_color
|
color: settings_popup.accent_color
|
||||||
font.pointSize: settings_popup.font_normal_text_size
|
font.pointSize: settings_popup.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
|
|
||||||
|
// Min Threshold
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Min Threshold: "
|
||||||
|
width: parent.width * 0.4
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
color: settings_popup.accent_color
|
color: settings_popup.accent_color
|
||||||
font.pointSize: settings_popup.font_normal_text_size
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: min_thres_input.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: min_thres_input
|
id: min_thres_input
|
||||||
text: config.min
|
text: config.min
|
||||||
anchors {
|
width: parent.width * 0.55
|
||||||
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: settings_popup.accent_color
|
color: settings_popup.accent_color
|
||||||
font.pointSize: settings_popup.font_normal_text_size
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alert Threshold
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Alert Threshold: "
|
||||||
|
width: parent.width * 0.4
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: alert_thres_input.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: alert_thres_input
|
id: alert_thres_input
|
||||||
text: config.alert
|
text: config.alert
|
||||||
anchors {
|
width: parent.width * 0.55
|
||||||
right: parent.right
|
|
||||||
rightMargin: 5
|
|
||||||
top: min_thres_label.bottom
|
|
||||||
topMargin: 20
|
|
||||||
}
|
|
||||||
width: parent.width / 2
|
|
||||||
color: settings_popup.accent_color
|
color: settings_popup.accent_color
|
||||||
font.pointSize: settings_popup.font_normal_text_size
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
|
||||||
id: interval_label
|
|
||||||
text: "Update Interval: "
|
|
||||||
anchors {
|
|
||||||
topMargin: 20
|
|
||||||
top: alert_thres_label.bottom
|
|
||||||
left: parent.left
|
|
||||||
verticalCenter: interval_input.verticalCenter
|
|
||||||
}
|
}
|
||||||
height: interval_input.height
|
|
||||||
|
// Update Interval
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Update Interval: "
|
||||||
|
width: parent.width * 0.4
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
color: settings_popup.accent_color
|
color: settings_popup.accent_color
|
||||||
font.pointSize: settings_popup.font_normal_text_size
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: interval_input.verticalCenter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: interval_input
|
id: interval_input
|
||||||
text: config.interval
|
text: config.interval
|
||||||
anchors {
|
width: parent.width * 0.55
|
||||||
right: parent.right
|
|
||||||
rightMargin: 5
|
|
||||||
top: alert_thres_input.bottom
|
|
||||||
topMargin: 20
|
|
||||||
}
|
|
||||||
width: parent.width / 2
|
|
||||||
color: settings_popup.accent_color
|
color: settings_popup.accent_color
|
||||||
font.pointSize: settings_popup.font_normal_text_size
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
|
||||||
id: http_url_label
|
|
||||||
text: "Url: "
|
|
||||||
anchors {
|
|
||||||
topMargin: 20
|
|
||||||
top: interval_label.bottom
|
|
||||||
left: parent.left
|
|
||||||
verticalCenter: http_url_input.verticalCenter
|
|
||||||
}
|
}
|
||||||
height: http_url_input.height
|
|
||||||
|
// URL
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Url: "
|
||||||
|
width: parent.width * 0.4
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
color: settings_popup.accent_color
|
color: settings_popup.accent_color
|
||||||
font.pointSize: settings_popup.font_normal_text_size
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: http_url_input.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: http_url_input
|
id: http_url_input
|
||||||
text: config.url
|
text: config.url
|
||||||
anchors {
|
width: parent.width * 0.55
|
||||||
right: parent.right
|
|
||||||
rightMargin: 20
|
|
||||||
leftMargin: 20
|
|
||||||
top: interval_input.bottom
|
|
||||||
topMargin: 20
|
|
||||||
}
|
|
||||||
width: Math.max(parent.width / 2, parent.width - http_url_label.width - anchors.leftMargin)
|
|
||||||
color: settings_popup.accent_color
|
color: settings_popup.accent_color
|
||||||
font.pointSize: settings_popup.font_normal_text_size
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
cursorPosition: 0
|
cursorPosition: 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tank Image Width
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Tank Image Width"
|
||||||
|
width: parent.width * 0.4
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: water_tank_width_input.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: water_tank_width_input
|
||||||
|
text: String(config.water_tank_width)
|
||||||
|
width: parent.width * 0.55
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
cursorPosition: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tank Image Height
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Tank Image Height"
|
||||||
|
width: parent.width * 0.4
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: water_tank_height_input.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: water_tank_height_input
|
||||||
|
text: String(config.water_tank_height)
|
||||||
|
width: parent.width * 0.55
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
cursorPosition: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accent Color
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Accent Color"
|
||||||
|
width: parent.width * 0.4
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: accent_color_input.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColorInput {
|
||||||
|
id: accent_color_input
|
||||||
|
text: config.accent_color
|
||||||
|
width: parent.width * 0.55
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark Accent Color
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Dark Accent Color"
|
||||||
|
width: parent.width * 0.4
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: dark_accent_color_input.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColorInput {
|
||||||
|
id: dark_accent_color_input
|
||||||
|
text: config.dark_accent_color
|
||||||
|
width: parent.width * 0.55
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Danger Color
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Danger Text Color"
|
||||||
|
width: parent.width * 0.4
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: danger_color_input.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColorInput {
|
||||||
|
id: danger_color_input
|
||||||
|
text: config.danger_color
|
||||||
|
width: parent.width * 0.55
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Water Color
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Water Color"
|
||||||
|
width: parent.width * 0.4
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: water_color_input.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColorInput {
|
||||||
|
id: water_color_input
|
||||||
|
text: config.water_color
|
||||||
|
width: parent.width * 0.55
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alert Water Color
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Alert Water Color"
|
||||||
|
width: parent.width * 0.4
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: water_color_above_alert_input.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColorInput {
|
||||||
|
id: water_color_above_alert_input
|
||||||
|
text: config.water_color_above_alert
|
||||||
|
width: parent.width * 0.55
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background Color
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Backgound Color"
|
||||||
|
width: parent.width * 0.4
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: background_color_input.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ColorInput {
|
||||||
|
id: background_color_input
|
||||||
|
text: config.background_color
|
||||||
|
width: parent.width * 0.55
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal Font Size
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Font Size"
|
||||||
|
width: parent.width * 0.4
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: font_normal_text_size_input.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: font_normal_text_size_input
|
||||||
|
text: String(config.font_normal_text_size)
|
||||||
|
width: parent.width * 0.55
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
cursorPosition: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header Font Size
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 10
|
||||||
|
Text {
|
||||||
|
text: "Header Font Size"
|
||||||
|
width: parent.width * 0.4
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
anchors {
|
||||||
|
verticalCenter: font_enlarged_text_size_input.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: font_enlarged_text_size_input
|
||||||
|
text: String(config.font_enlarged_text_size)
|
||||||
|
width: parent.width * 0.55
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
cursorPosition: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckBox
|
||||||
|
CustomCheckBox {
|
||||||
|
id: current_reading_enable
|
||||||
|
checked: config.current_reading_enable
|
||||||
|
font_color: settings_popup.accent_color
|
||||||
|
font_point_size: settings_popup.font_normal_text_size
|
||||||
|
text: "Enable Current Reading"
|
||||||
|
leftPadding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckBox
|
||||||
|
CustomCheckBox {
|
||||||
|
id: percentage_enable
|
||||||
|
checked: config.percentage_enable
|
||||||
|
font_color: settings_popup.accent_color
|
||||||
|
font_point_size: settings_popup.font_normal_text_size
|
||||||
|
text: "Enable Percentage Reading"
|
||||||
|
leftPadding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckBox
|
||||||
|
CustomCheckBox {
|
||||||
|
id: filling_status_enable
|
||||||
|
checked: config.filling_status_enable
|
||||||
|
font_color: settings_popup.accent_color
|
||||||
|
font_point_size: settings_popup.font_normal_text_size
|
||||||
|
text: "Enable Filling Status"
|
||||||
|
leftPadding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckBox
|
||||||
|
CustomCheckBox {
|
||||||
|
id: rate_of_fill_enable
|
||||||
|
checked: config.rate_of_fill_enable
|
||||||
|
font_color: settings_popup.accent_color
|
||||||
|
font_point_size: settings_popup.font_normal_text_size
|
||||||
|
text: "Enable Text Rate of Fill"
|
||||||
|
leftPadding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckBox
|
||||||
|
CustomCheckBox {
|
||||||
|
id: expected_to_fill_enable
|
||||||
|
checked: config.expected_to_fill_enable
|
||||||
|
font_color: settings_popup.accent_color
|
||||||
|
font_point_size: settings_popup.font_normal_text_size
|
||||||
|
text: "Enable Expected to fill"
|
||||||
|
leftPadding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectButton {
|
||||||
|
id: delete_config_button
|
||||||
|
width: parent.width / 1.5
|
||||||
|
height: 80
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
font_color: settings_popup.accent_color
|
||||||
|
font_enlarged_text_size: settings_popup.font_enlarged_text_size
|
||||||
|
font_normal_text_size: settings_popup.font_normal_text_size
|
||||||
|
color: settings_popup.background_color
|
||||||
|
accent_color: settings_popup.accent_color
|
||||||
|
text: "Delete/Reset Config"
|
||||||
|
text2: text
|
||||||
|
onConnect: {
|
||||||
|
settings_popup.deleteConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: save_config
|
id: save_config
|
||||||
anchors {
|
anchors {
|
||||||
|
|
@ -237,7 +545,43 @@ Popup {
|
||||||
let alert = parseFloat(alert_thres_input.text);
|
let alert = parseFloat(alert_thres_input.text);
|
||||||
let url = http_url_input.text;
|
let url = http_url_input.text;
|
||||||
let interval = parseInt(interval_input.text);
|
let interval = parseInt(interval_input.text);
|
||||||
var new_config = {"max": max, "min": min, "alert": alert, "url": url, "interval": interval}
|
let image_width = parseFloat(water_tank_width_input.text);
|
||||||
|
let image_height = parseFloat(water_tank_height_input.text);
|
||||||
|
|
||||||
|
let current_reading = current_reading_enable.checked;
|
||||||
|
let percentage_reading = percentage_enable.checked
|
||||||
|
let filling_status = filling_status_enable.checked;
|
||||||
|
let rate_of_fill = rate_of_fill_enable.checked;
|
||||||
|
let expected_to_fill = expected_to_fill_enable.checked;
|
||||||
|
|
||||||
|
let normal_font_size = parseInt(font_normal_text_size_input.text);
|
||||||
|
let enlarged_font_size = parseInt(font_enlarged_text_size_input.text);
|
||||||
|
|
||||||
|
let accent_color = accent_color_input.text;
|
||||||
|
let dark_accent_color = dark_accent_color_input.text;
|
||||||
|
let danger_color = danger_color_input.text;
|
||||||
|
let water_color = water_color_input.text;
|
||||||
|
let water_color_above_alert = water_color_above_alert_input.text;
|
||||||
|
let background_color = background_color_input.text;
|
||||||
|
|
||||||
|
var new_config = {"max": max, "min": min, "alert": alert, "url": url,
|
||||||
|
"interval": interval,
|
||||||
|
"water_tank_height": image_height,
|
||||||
|
"water_tank_width": image_width,
|
||||||
|
"current_reading_enable": current_reading,
|
||||||
|
"percentage_enable": percentage_reading,
|
||||||
|
"filling_status_enable": filling_status,
|
||||||
|
"rate_of_fill_enable": rate_of_fill,
|
||||||
|
"expected_to_fill_enable": expected_to_fill,
|
||||||
|
"font_normal_text_size": normal_font_size,
|
||||||
|
"font_enlarged_text_size": enlarged_font_size,
|
||||||
|
"accent_color": accent_color,
|
||||||
|
"dark_accent_color": dark_accent_color,
|
||||||
|
"danger_color": danger_color,
|
||||||
|
"water_color": water_color,
|
||||||
|
"water_color_above_alert": water_color_above_alert,
|
||||||
|
"background_color": background_color
|
||||||
|
}
|
||||||
settings_popup.saveConfig(new_config)
|
settings_popup.saveConfig(new_config)
|
||||||
settings_popup.close();
|
settings_popup.close();
|
||||||
}
|
}
|
||||||
|
|
@ -245,7 +589,6 @@ Popup {
|
||||||
save_config.color = settings_rect_pressed_prev
|
save_config.color = settings_rect_pressed_prev
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,9 +9,12 @@ Item {
|
||||||
property color tank_lid_color: "#F0121212"
|
property color tank_lid_color: "#F0121212"
|
||||||
property color tank_base_color: "#00FFFFFF"
|
property color tank_base_color: "#00FFFFFF"
|
||||||
property color water_level_color: "#F00F7FAF"
|
property color water_level_color: "#F00F7FAF"
|
||||||
|
property color water_color_above_alert: "#F00FAF0F"
|
||||||
property color tank_base_border_color: "#F0121212"
|
property color tank_base_border_color: "#F0121212"
|
||||||
property color text_color: "#F05FAF5F"
|
property color text_color: "#F05FAF5F"
|
||||||
property double height_max: tank_base.height - 10
|
property double height_max: tank_base.height - 10
|
||||||
|
property int font_normal_text_size: 0
|
||||||
|
property int font_enlarged_text_size: 0
|
||||||
property double current_level: 0
|
property double current_level: 0
|
||||||
property double percentage_filled: 0
|
property double percentage_filled: 0
|
||||||
property int distance: 0
|
property int distance: 0
|
||||||
|
|
@ -19,6 +22,12 @@ Item {
|
||||||
property double max_thres_input: 0
|
property double max_thres_input: 0
|
||||||
property double rate_of_fill: 0
|
property double rate_of_fill: 0
|
||||||
property double expected_to_fill: 0
|
property double expected_to_fill: 0
|
||||||
|
property double alert_threshold: 0
|
||||||
|
property bool enable_current_reading: true
|
||||||
|
property bool enable_percentage_reading: true
|
||||||
|
property bool enable_filling_status: true
|
||||||
|
property bool enable_rate_of_fill: true
|
||||||
|
property bool enable_expected_to_fill: true
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: base
|
id: base
|
||||||
|
|
@ -28,7 +37,7 @@ Item {
|
||||||
id: tank_lid
|
id: tank_lid
|
||||||
color: root.tank_lid_color
|
color: root.tank_lid_color
|
||||||
anchors {
|
anchors {
|
||||||
bottom: tank_base.top
|
top: parent.top
|
||||||
horizontalCenter: tank_base.horizontalCenter
|
horizontalCenter: tank_base.horizontalCenter
|
||||||
bottomMargin: -5
|
bottomMargin: -5
|
||||||
}
|
}
|
||||||
|
|
@ -40,9 +49,12 @@ Item {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: tank_base
|
id: tank_base
|
||||||
color: root.tank_base_color
|
color: root.tank_base_color
|
||||||
anchors.centerIn: parent
|
anchors {
|
||||||
|
top: tank_lid.bottom
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height * 0.55
|
||||||
border.color: root.tank_base_border_color
|
border.color: root.tank_base_border_color
|
||||||
border.width: 5
|
border.width: 5
|
||||||
radius: 4
|
radius: 4
|
||||||
|
|
@ -70,14 +82,29 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
ScrollView {
|
||||||
id: water_level_reading
|
id: rect_base_scroll_view
|
||||||
text: "Current Reading: " + (root.distance / 10)+ " cm"
|
|
||||||
font.pointSize: 16
|
|
||||||
color: root.text_color
|
|
||||||
anchors {
|
anchors {
|
||||||
margins: 5
|
margins: 5
|
||||||
top: tank_base.bottom
|
top: tank_base.bottom
|
||||||
|
bottom: parent.bottom
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
contentWidth: parent.width
|
||||||
|
contentHeight: settings_column.implicitHeight
|
||||||
|
Column {
|
||||||
|
id: settings_column
|
||||||
|
spacing: 10
|
||||||
|
width: rect_base_scroll_view.width
|
||||||
|
Text {
|
||||||
|
id: water_level_reading
|
||||||
|
text: "Current Reading: " + (root.distance / 10)+ " cm"
|
||||||
|
font.pointSize: root.font_normal_text_size
|
||||||
|
color: root.text_color
|
||||||
|
visible: enable_current_reading
|
||||||
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
@ -85,12 +112,11 @@ Item {
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: water_level_percentage
|
id: water_level_percentage
|
||||||
text: "Percentage: " + root.percentage_filled + " %"
|
text: "Percentage: " + root.percentage_filled.toFixed(2) + " %"
|
||||||
font.pointSize: 16
|
font.pointSize: root.font_normal_text_size
|
||||||
|
visible: enable_percentage_reading
|
||||||
color: root.text_color
|
color: root.text_color
|
||||||
anchors {
|
anchors {
|
||||||
margins: 5
|
|
||||||
top: water_level_reading.bottom
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
@ -99,11 +125,10 @@ Item {
|
||||||
Text {
|
Text {
|
||||||
id: fill_status
|
id: fill_status
|
||||||
text: "Filling status: " + (( root.rate_of_fill > 0)? "Filling" : "Discharging")
|
text: "Filling status: " + (( root.rate_of_fill > 0)? "Filling" : "Discharging")
|
||||||
font.pointSize: 16
|
font.pointSize: root.font_normal_text_size
|
||||||
color: root.text_color
|
color: root.text_color
|
||||||
|
visible: enable_filling_status
|
||||||
anchors {
|
anchors {
|
||||||
margins: 5
|
|
||||||
top: water_level_percentage.bottom
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
@ -111,11 +136,10 @@ Item {
|
||||||
Text {
|
Text {
|
||||||
id: rate_of_fill_label
|
id: rate_of_fill_label
|
||||||
text: "Rate of fill: " + root.rate_of_fill + " cm/s"
|
text: "Rate of fill: " + root.rate_of_fill + " cm/s"
|
||||||
font.pointSize: 16
|
font.pointSize: root.font_normal_text_size
|
||||||
color: root.text_color
|
color: root.text_color
|
||||||
|
visible: enable_rate_of_fill
|
||||||
anchors {
|
anchors {
|
||||||
margins: 5
|
|
||||||
top: fill_status.bottom
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
@ -123,20 +147,28 @@ Item {
|
||||||
Text {
|
Text {
|
||||||
id: expected_to_fill_label
|
id: expected_to_fill_label
|
||||||
text: "Expected to fill in: " + root.expected_to_fill + " s"
|
text: "Expected to fill in: " + root.expected_to_fill + " s"
|
||||||
font.pointSize: 16
|
font.pointSize: root.font_normal_text_size
|
||||||
color: root.text_color
|
color: root.text_color
|
||||||
visible: (root.rate_of_fill > 0) ? true : false
|
visible: (root.rate_of_fill > 0 && enable_expected_to_fill === true) ? true : false
|
||||||
anchors {
|
anchors {
|
||||||
margins: 5
|
|
||||||
top: rate_of_fill_label.bottom
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
}
|
}
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
function setConfig(config: var) {
|
function setConfig(config: var) {
|
||||||
min_thres_input = config.min;
|
min_thres_input = config.min;
|
||||||
max_thres_input = config.max;
|
max_thres_input = config.max;
|
||||||
|
alert_threshold = config.alert;
|
||||||
|
enable_current_reading = config.current_reading_enable;
|
||||||
|
enable_percentage_reading = config.percentage_enable;
|
||||||
|
enable_filling_status = config.filling_status_enable;
|
||||||
|
enable_rate_of_fill = config.rate_of_fill_enable;
|
||||||
|
enable_expected_to_fill = config.expected_to_fill_enable;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDistance(distance) {
|
function setDistance(distance) {
|
||||||
|
|
@ -156,8 +188,17 @@ Item {
|
||||||
console.log("Current reading is: " + current_reading)
|
console.log("Current reading is: " + current_reading)
|
||||||
console.log("Normalized height is: " + normalized_height)
|
console.log("Normalized height is: " + normalized_height)
|
||||||
console.log("Percentage filled is: " + percentage_filled)
|
console.log("Percentage filled is: " + percentage_filled)
|
||||||
|
console.log("Alert threshold is: " + root.alert_threshold)
|
||||||
|
console.log("Is level above alert threshold: " + (distance_f <= root.alert_threshold))
|
||||||
root.current_level = normalized_height;
|
root.current_level = normalized_height;
|
||||||
root.percentage_filled = percentage_filled;
|
root.percentage_filled = percentage_filled;
|
||||||
|
|
||||||
|
if(distance_f <= root.alert_threshold) {
|
||||||
|
water_level.color = water_color_above_alert
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
water_level.color = water_level_color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
61
android/AndroidManifest.xml
Normal file
61
android/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.qtproject.example.appWaterLevelRewrite"
|
||||||
|
android:installLocation="auto"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0">
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
|
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" android:usesPermissionFlags="neverForLocation" />
|
||||||
|
<supports-screens
|
||||||
|
android:anyDensity="true"
|
||||||
|
android:largeScreens="true"
|
||||||
|
android:normalScreens="true"
|
||||||
|
android:smallScreens="true" />
|
||||||
|
<application
|
||||||
|
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:label="appWaterLevelRewrite"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:enableOnBackInvokedCallback="false"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:fullBackupOnly="false">
|
||||||
|
<activity
|
||||||
|
android:name="org.qtproject.qt.android.bindings.QtActivity"
|
||||||
|
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||||
|
android:label="appWaterLevelRewrite"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:screenOrientation="unspecified"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.lib_name"
|
||||||
|
android:value="appWaterLevelRewrite" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.arguments"
|
||||||
|
android:value="" />
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.qtprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/qtprovider_paths"/>
|
||||||
|
</provider>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
ConfigInterface::ConfigInterface(QObject *parent)
|
ConfigInterface::ConfigInterface(QObject *parent)
|
||||||
: QObject{parent}
|
: QObject{parent}
|
||||||
|
|
@ -37,6 +38,12 @@ void ConfigInterface::setFileName(const QString &newFileName)
|
||||||
emit fileNameChanged();
|
emit fileNameChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigInterface::deleteConfig()
|
||||||
|
{
|
||||||
|
std::filesystem::remove(std::filesystem::path(m_fileName.toStdString()));
|
||||||
|
emit configDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
void ConfigInterface::saveConfig(QJsonObject config)
|
void ConfigInterface::saveConfig(QJsonObject config)
|
||||||
{
|
{
|
||||||
QJsonDocument doc;
|
QJsonDocument doc;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@ public:
|
||||||
signals:
|
signals:
|
||||||
void config(QJsonObject config);
|
void config(QJsonObject config);
|
||||||
void fileNameChanged();
|
void fileNameChanged();
|
||||||
|
void configDeleted();
|
||||||
public slots:
|
public slots:
|
||||||
|
void deleteConfig();
|
||||||
void saveConfig(QJsonObject config);
|
void saveConfig(QJsonObject config);
|
||||||
private:
|
private:
|
||||||
QString m_fileName;
|
QString m_fileName;
|
||||||
|
|
|
||||||
BIN
echo.mp3
Normal file
BIN
echo.mp3
Normal file
Binary file not shown.
3
main.cpp
3
main.cpp
|
|
@ -3,8 +3,11 @@
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
qputenv("QT_SCALE_FACTOR", "1.0");
|
||||||
|
qputenv("QT_SCALE_FACTOR_ROUNDING_POLICY", "Round");
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
|
|
||||||
|
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
&engine,
|
&engine,
|
||||||
|
|
|
||||||
5
media.qrc
Normal file
5
media.qrc
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<RCC>
|
||||||
|
<qresource prefix="/sounds">
|
||||||
|
<file>echo.mp3</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
|
|
@ -3,11 +3,22 @@
|
||||||
#include <QJsonValue>
|
#include <QJsonValue>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
RequestWorker::RequestWorker(QObject *parent)
|
RequestWorker::RequestWorker(QObject *parent)
|
||||||
: QObject{parent}
|
: QObject{parent}
|
||||||
{
|
{
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
wifi = new WiFiHelper(this);
|
||||||
|
connect(wifi, &WiFiHelper::ssidRetrieved, this, &RequestWorker::gotWiFiSSID);
|
||||||
|
poll_wifi = new QTimer(this);
|
||||||
|
poll_wifi->setInterval(3000);
|
||||||
|
poll_wifi->setSingleShot(false);
|
||||||
|
connect(poll_wifi, &QTimer::timeout, this, &RequestWorker::pollWiFi);
|
||||||
|
wifi->requestPermissionsAndFetch();
|
||||||
|
poll_wifi->start();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestWorker::setConfig(QJsonObject config)
|
void RequestWorker::setConfig(QJsonObject config)
|
||||||
|
|
@ -16,11 +27,13 @@ void RequestWorker::setConfig(QJsonObject config)
|
||||||
this->interval = config["interval"].toInt();
|
this->interval = config["interval"].toInt();
|
||||||
this->max_height = config["max"].toInt();
|
this->max_height = config["max"].toInt();
|
||||||
this->min_height = config["min"].toInt();
|
this->min_height = config["min"].toInt();
|
||||||
|
this->alarm_thres = config["alert"].toInt();
|
||||||
qDebug() << "In cpp config values are: ";
|
qDebug() << "In cpp config values are: ";
|
||||||
qDebug() << "Url: " << url;
|
qDebug() << "Url: " << url;
|
||||||
qDebug() << "Interval: " << interval;
|
qDebug() << "Interval: " << interval;
|
||||||
qDebug() << "Max Height: " << max_height;
|
qDebug() << "Max Height: " << max_height;
|
||||||
qDebug() << "Min Height: " << min_height;
|
qDebug() << "Min Height: " << min_height;
|
||||||
|
qDebug() << "Alert: " << alarm_thres;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestWorker::start()
|
void RequestWorker::start()
|
||||||
|
|
@ -51,6 +64,129 @@ void RequestWorker::stop()
|
||||||
qDebug() << "Stopping" << timer->isActive();
|
qDebug() << "Stopping" << timer->isActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString RequestWorker::getRequestResponseString(QNetworkReply *rep, QString rep_data)
|
||||||
|
{
|
||||||
|
if(!rep) {
|
||||||
|
return "No Reply";
|
||||||
|
}
|
||||||
|
QNetworkRequest req = rep->request();
|
||||||
|
QString errorString = rep->errorString();
|
||||||
|
QString resp = "GET " + req.url().toString() + "\n";
|
||||||
|
for(auto &key: req.rawHeaderList()) {
|
||||||
|
QByteArray value = req.rawHeader(key);
|
||||||
|
resp += key + ": " + value + "\n";
|
||||||
|
}
|
||||||
|
qDebug() << "Raw header list size: " << req.rawHeaderList().size();
|
||||||
|
QString ret;
|
||||||
|
ret = "Request:\n";
|
||||||
|
ret += resp;
|
||||||
|
ret += "Response:\n";
|
||||||
|
if(rep_data == "")
|
||||||
|
ret += errorString;
|
||||||
|
else
|
||||||
|
ret += rep_data;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestWorker::pollWiFi()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
if(wifi) {
|
||||||
|
wifi->requestPermissionsAndFetch();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestWorker::gotWiFiSSID(QString ssid)
|
||||||
|
{
|
||||||
|
if(ssid.length() > 0 && ssid != "Permission Denied" && ssid != "<unknown ssid>") {
|
||||||
|
emit connectionText("WiFi connected: " + ssid);
|
||||||
|
}
|
||||||
|
else if(ssid.length() == 0) {
|
||||||
|
emit connectionText("WiFi might not be connected", true);
|
||||||
|
}
|
||||||
|
else if(ssid != "<unknown ssid>") {
|
||||||
|
emit connectionText("Failed to query wifi info", true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
emit connectionText("WiFi disconnected", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestWorker::playAlarm()
|
||||||
|
{
|
||||||
|
if (player_started)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Step 1: Copy the qrc resource to a local cache file so Android can read it
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
QString qrcPath = ":/sounds/echo.mp3";
|
||||||
|
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/echo.mp3";
|
||||||
|
|
||||||
|
if (QFile::exists(cachePath)) {
|
||||||
|
QFile::remove(cachePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QFile::copy(qrcPath, cachePath)) {
|
||||||
|
qDebug() << "Successfully copied resource to cache:" << cachePath;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Failed to copy resource to cache path!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Use Android JNI to initialize and play via android.media.MediaPlayer
|
||||||
|
QJniEnvironment env;
|
||||||
|
|
||||||
|
// Create new android.media.MediaPlayer()
|
||||||
|
QJniObject mediaPlayer("android/media/MediaPlayer");
|
||||||
|
if (!mediaPlayer.isValid()) {
|
||||||
|
qDebug() << "Failed to create native Android MediaPlayer!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set data source using the local cache file path
|
||||||
|
QJniObject jPath = QJniObject::fromString(cachePath);
|
||||||
|
mediaPlayer.callMethod<void>("setDataSource", "(Ljava/lang/String;)V", jPath.object<jstring>());
|
||||||
|
|
||||||
|
// Enable looping
|
||||||
|
mediaPlayer.callMethod<void>("setLooping", "(Z)V", true);
|
||||||
|
|
||||||
|
// Prepare and start playback
|
||||||
|
mediaPlayer.callMethod<void>("prepare");
|
||||||
|
mediaPlayer.callMethod<void>("start");
|
||||||
|
|
||||||
|
// Store a global reference so it doesn't get garbage collected immediately
|
||||||
|
m_androidMediaPlayer = new QJniObject(mediaPlayer.object());
|
||||||
|
|
||||||
|
player_started = true;
|
||||||
|
qDebug() << "Native Android alarm started successfully.";
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
if(player) {
|
||||||
|
player->deleteLater();
|
||||||
|
player = nullptr;
|
||||||
|
}
|
||||||
|
if(audioOutput) {
|
||||||
|
audioOutput->deleteLater();
|
||||||
|
audioOutput->deleteLater();
|
||||||
|
}
|
||||||
|
qDebug() << "Loading player";
|
||||||
|
player = new QMediaPlayer(this);
|
||||||
|
audioOutput = new QAudioOutput(this);
|
||||||
|
player->setLoops(QMediaPlayer::Loops::Infinite);
|
||||||
|
player->setAudioOutput(audioOutput);
|
||||||
|
if (QFile::exists(":/sounds/echo.mp3")) {
|
||||||
|
qDebug() << "Resource found successfully!";
|
||||||
|
} else {
|
||||||
|
qDebug() << "Resource not found. Check CMakeLists.txt or .pro file!";
|
||||||
|
}
|
||||||
|
player->setSource(QUrl("qrc:/sounds/echo.mp3"));
|
||||||
|
audioOutput->setVolume(0.5);
|
||||||
|
connect(this, &RequestWorker::playMedia, player, &QMediaPlayer::play);
|
||||||
|
player_started = true;
|
||||||
|
emit playMedia();
|
||||||
|
}
|
||||||
|
|
||||||
void RequestWorker::makeRequest()
|
void RequestWorker::makeRequest()
|
||||||
{
|
{
|
||||||
if(stop_timer)
|
if(stop_timer)
|
||||||
|
|
@ -67,15 +203,16 @@ void RequestWorker::makeRequest()
|
||||||
void RequestWorker::requestFinished(QNetworkReply *rep)
|
void RequestWorker::requestFinished(QNetworkReply *rep)
|
||||||
{
|
{
|
||||||
QByteArray data = rep->readAll();
|
QByteArray data = rep->readAll();
|
||||||
rep->deleteLater();
|
|
||||||
rep = nullptr;
|
|
||||||
auto now = std::chrono::high_resolution_clock::now();
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
auto time_diff = std::chrono::duration_cast<std::chrono::microseconds>(now - prev_time);
|
auto time_diff = std::chrono::duration_cast<std::chrono::microseconds>(now - prev_time);
|
||||||
if(data.size() == 0) {
|
if(data.size() == 0) {
|
||||||
prev_time = now;
|
prev_time = now;
|
||||||
timer->start();
|
timer->start();
|
||||||
emit requestFailed();
|
QString request_response_string = getRequestResponseString(rep);
|
||||||
|
emit requestFailed(request_response_string);
|
||||||
emit setStatus("Invalid Response; Resending Request");
|
emit setStatus("Invalid Response; Resending Request");
|
||||||
|
rep->deleteLater();
|
||||||
|
rep = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsigned int time_in_microseconds = time_diff.count();
|
unsigned int time_in_microseconds = time_diff.count();
|
||||||
|
|
@ -85,6 +222,13 @@ void RequestWorker::requestFinished(QNetworkReply *rep)
|
||||||
float rate_of_fill = ( float )(prev_distance - water_level) /(float) time_in_microseconds;
|
float rate_of_fill = ( float )(prev_distance - water_level) /(float) time_in_microseconds;
|
||||||
rate_of_fill = rate_of_fill * 1'000'000;
|
rate_of_fill = rate_of_fill * 1'000'000;
|
||||||
int expected_to_fill = (water_level - min_height) / rate_of_fill;
|
int expected_to_fill = (water_level - min_height) / rate_of_fill;
|
||||||
|
int percentage_filled = (float (max_height - water_level) / float (max_height)) * 100;
|
||||||
|
if(enable_alarm) {
|
||||||
|
qDebug() << "Alarm enabled";
|
||||||
|
if(water_level <= alarm_thres) {
|
||||||
|
playAlarm();
|
||||||
|
}
|
||||||
|
}
|
||||||
if(reply_number == 0) {
|
if(reply_number == 0) {
|
||||||
rate_of_fill = 0;
|
rate_of_fill = 0;
|
||||||
expected_to_fill = 0;
|
expected_to_fill = 0;
|
||||||
|
|
@ -93,16 +237,22 @@ void RequestWorker::requestFinished(QNetworkReply *rep)
|
||||||
qDebug() << "Got water level: " << water_level;
|
qDebug() << "Got water level: " << water_level;
|
||||||
qDebug() << "Got rate of fill: " << rate_of_fill;
|
qDebug() << "Got rate of fill: " << rate_of_fill;
|
||||||
qDebug() << "Got expected to fill: " << expected_to_fill;
|
qDebug() << "Got expected to fill: " << expected_to_fill;
|
||||||
|
qDebug() << "Percentage filled: " << percentage_filled;
|
||||||
qDebug() << "Time difference: " << time_in_microseconds;
|
qDebug() << "Time difference: " << time_in_microseconds;
|
||||||
prev_distance = water_level;
|
prev_distance = water_level;
|
||||||
emit networkReply(data_water_level, rate_of_fill, expected_to_fill);
|
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");
|
||||||
|
QString request_response_string = getRequestResponseString(rep, data);
|
||||||
|
request_response_string += "Timestamp:\n" + timestamp;
|
||||||
|
rep->deleteLater();
|
||||||
|
rep = nullptr;
|
||||||
|
emit networkReply(data_water_level, rate_of_fill, expected_to_fill, request_response_string);
|
||||||
if(stop_timer) {
|
if(stop_timer) {
|
||||||
setStatus("Timer stopped.");
|
setStatus("Timer stopped.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
prev_time = now;
|
prev_time = now;
|
||||||
timer->start();
|
timer->start();
|
||||||
QString status = "Got Reply on: " + QDateTime::currentDateTime().toString("hh:mm:ss") + "; Waiting to Send.";
|
QString status = "Got Reply on: " + timestamp + "; Waiting to Send.";
|
||||||
setStatus(status);
|
setStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,3 +268,35 @@ void RequestWorker::setStatus(const QString &newStatus)
|
||||||
m_status = newStatus;
|
m_status = newStatus;
|
||||||
emit statusChanged();
|
emit statusChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RequestWorker::startAlarm()
|
||||||
|
{
|
||||||
|
enable_alarm = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestWorker::stopAlarm()
|
||||||
|
{
|
||||||
|
if (!player_started)
|
||||||
|
return;
|
||||||
|
|
||||||
|
enable_alarm = false;
|
||||||
|
player_started = false;
|
||||||
|
if(player) {
|
||||||
|
player->stop();
|
||||||
|
player->deleteLater();
|
||||||
|
player = nullptr;
|
||||||
|
}
|
||||||
|
if(audioOutput) {
|
||||||
|
audioOutput->deleteLater();
|
||||||
|
audioOutput = nullptr;
|
||||||
|
}
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
if (m_androidMediaPlayer && m_androidMediaPlayer->isValid()) {
|
||||||
|
m_androidMediaPlayer->callMethod<void>("stop");
|
||||||
|
m_androidMediaPlayer->callMethod<void>("release");
|
||||||
|
|
||||||
|
delete m_androidMediaPlayer;
|
||||||
|
m_androidMediaPlayer = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,15 @@
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
#include <QMediaPlayer>
|
||||||
|
#include <QAudioOutput>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
#include "wifihelper.h"
|
||||||
|
#include <QJniObject>
|
||||||
|
#endif
|
||||||
|
|
||||||
class RequestWorker : public QObject
|
class RequestWorker : public QObject
|
||||||
{
|
{
|
||||||
|
|
@ -24,27 +30,45 @@ class RequestWorker : public QObject
|
||||||
int min_height = 0;
|
int min_height = 0;
|
||||||
bool stop_timer = false;
|
bool stop_timer = false;
|
||||||
int reply_number = 0;
|
int reply_number = 0;
|
||||||
|
int alarm_thres = 0;
|
||||||
|
bool enable_alarm = false;
|
||||||
|
bool player_started = false;
|
||||||
|
QMediaPlayer *player = nullptr;
|
||||||
|
QAudioOutput *audioOutput = nullptr;
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
WiFiHelper *wifi = nullptr;
|
||||||
|
QTimer *poll_wifi = nullptr;
|
||||||
|
#endif
|
||||||
std::chrono::time_point<std::chrono::high_resolution_clock> prev_time;
|
std::chrono::time_point<std::chrono::high_resolution_clock> prev_time;
|
||||||
public:
|
public:
|
||||||
explicit RequestWorker(QObject *parent = nullptr);
|
explicit RequestWorker(QObject *parent = nullptr);
|
||||||
|
|
||||||
QString status() const;
|
QString status() const;
|
||||||
void setStatus(const QString &newStatus);
|
void setStatus(const QString &newStatus);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void requestFailed();
|
void requestFailed(QString request_response_string);
|
||||||
void networkReply(int distance, float rate_of_fill, int expected_to_fill);
|
void networkReply(int distance, float rate_of_fill, int expected_to_fill, QString data);
|
||||||
void statusChanged();
|
void statusChanged();
|
||||||
|
void playMedia();
|
||||||
|
void connectionText(QString text, bool errored = false);
|
||||||
public slots:
|
public slots:
|
||||||
|
void startAlarm();
|
||||||
|
void stopAlarm();
|
||||||
void setConfig(QJsonObject config);
|
void setConfig(QJsonObject config);
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
private slots:
|
private slots:
|
||||||
|
QString getRequestResponseString(QNetworkReply *rep, QString rep_data = "");
|
||||||
|
void pollWiFi();
|
||||||
|
void gotWiFiSSID(QString ssid);
|
||||||
|
void playAlarm();
|
||||||
void makeRequest();
|
void makeRequest();
|
||||||
void requestFinished(QNetworkReply *rep);
|
void requestFinished(QNetworkReply *rep);
|
||||||
private:
|
private:
|
||||||
QString m_status;
|
QString m_status;
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
QJniObject *m_androidMediaPlayer = nullptr;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // REQUESTWORKER_H
|
#endif // REQUESTWORKER_H
|
||||||
|
|
|
||||||
77
wifihelper.cpp
Normal file
77
wifihelper.cpp
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
#include "wifihelper.h"
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
#include <QJniObject>
|
||||||
|
#endif
|
||||||
|
#include <QPermissions>
|
||||||
|
|
||||||
|
WiFiHelper::WiFiHelper(QObject *parent)
|
||||||
|
: QObject{parent}
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WiFiHelper::getWifiSsid() {
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
// Get Android application context in Qt 6
|
||||||
|
QJniObject context = QNativeInterface::QAndroidApplication::context();
|
||||||
|
if (!context.isValid()) return QString();
|
||||||
|
|
||||||
|
// Get WifiManager service
|
||||||
|
QJniObject wifiService = QJniObject::fromString("wifi");
|
||||||
|
QJniObject wifiManager = context.callObjectMethod(
|
||||||
|
"getSystemService",
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/Object;",
|
||||||
|
wifiService.object()
|
||||||
|
);
|
||||||
|
if (!wifiManager.isValid()) return QString();
|
||||||
|
|
||||||
|
// Fetch WifiInfo
|
||||||
|
QJniObject wifiInfo = wifiManager.callObjectMethod(
|
||||||
|
"getConnectionInfo",
|
||||||
|
"()Landroid/net/wifi/WifiInfo;"
|
||||||
|
);
|
||||||
|
if (!wifiInfo.isValid()) return QString();
|
||||||
|
|
||||||
|
// Get SSID string
|
||||||
|
QJniObject ssidJni = wifiInfo.callObjectMethod(
|
||||||
|
"getSSID",
|
||||||
|
"()Ljava/lang/String;"
|
||||||
|
);
|
||||||
|
if (!ssidJni.isValid()) return QString();
|
||||||
|
|
||||||
|
QString ssid = ssidJni.toString();
|
||||||
|
|
||||||
|
// Android returns SSIDs wrapped in double quotes (e.g., "\"MyWiFi\"")
|
||||||
|
if (ssid.startsWith('"') && ssid.endsWith('"') && ssid.length() > 1) {
|
||||||
|
ssid = ssid.mid(1, ssid.length() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ssid;
|
||||||
|
#endif
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WiFiHelper::requestPermissionsAndFetch() {
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
QLocationPermission locationPermission;
|
||||||
|
locationPermission.setAccuracy(QLocationPermission::Accuracy::Precise);
|
||||||
|
Qt::PermissionStatus status = qApp->checkPermission(locationPermission);
|
||||||
|
|
||||||
|
if (status == Qt::PermissionStatus::Granted) {
|
||||||
|
emit ssidRetrieved(getWifiSsid());
|
||||||
|
} else {
|
||||||
|
qDebug() << "Requesting permission.";
|
||||||
|
qApp->requestPermission(locationPermission, this, [this](const QPermission &permission) {
|
||||||
|
if (permission.status() == Qt::PermissionStatus::Granted) {
|
||||||
|
qDebug() << "Requested permission and got ssid";
|
||||||
|
emit ssidRetrieved(getWifiSsid());
|
||||||
|
} else {
|
||||||
|
qDebug() << "Failed to get WiFi SSID due to denied permission.";
|
||||||
|
emit ssidRetrieved("Permission Denied");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
17
wifihelper.h
Normal file
17
wifihelper.h
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef WIFIHELPER_H
|
||||||
|
#define WIFIHELPER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class WiFiHelper : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit WiFiHelper(QObject *parent = nullptr);
|
||||||
|
Q_INVOKABLE QString getWifiSsid();
|
||||||
|
Q_INVOKABLE void requestPermissionsAndFetch();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void ssidRetrieved(const QString &ssid);
|
||||||
|
};
|
||||||
|
#endif // WIFIHELPER_H
|
||||||
Loading…
Reference in a new issue