This repository has been archived on 2024-06-03. You can view files and clone it, but cannot push or open issues or pull requests.
app/qml/main.qml

826 lines
29 KiB
QML
Raw Normal View History

2018-08-12 20:51:57 +02:00
/*
Speed Climbing Stopwatch - Simple Stopwatch for Climbers
Copyright (C) 2018 Itsblue Development - Dorian Zeder
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
2018-07-17 19:17:25 +02:00
import QtQuick 2.9
import QtMultimedia 5.8
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
import "."
import "./components"
import "./styles"
2018-07-22 21:08:11 +02:00
//import QtQuick.Layouts 1.11
2018-07-22 16:47:55 +02:00
import com.itsblue.speedclimbingstopwatch 1.0
2018-07-17 19:17:25 +02:00
Window {
visible: true
width: 540
height: 960
title: qsTr("Speedclimbing stw")
2018-07-17 19:17:25 +02:00
property date currentTime: new Date()
property int millis: 0
onBeforeRendering: {
StyleSettings.refreshTheme()
}
2018-07-17 19:17:25 +02:00
Page {
id:root
anchors.fill: parent
property double startTime: 0
property double stoppedTime: 0
2018-07-26 14:54:11 +02:00
property double currTime
2018-07-17 19:17:25 +02:00
property double buzzer_offset: buzzerConn.offset
property double last_button_pressed: buzzerConn.lastTriggered
property var last_run : {
'stop_type': "", 'time': 0, 'react_time': 0
};
//array that contains all connections an their atatus
property var connections: {
'buzzer': buzzerConn.status,
'startpad': startpadConn.status,
'baseStation': baseConn.status
}
//set default state to IDLE
2018-07-22 16:47:55 +02:00
state: "IDLE"
Rectangle {
id: backgroundRect
anchors.fill: parent
color: StyleSettings.backgroundColor
}
Item {
id: connections
BuzzerConn {
id: buzzerConn
ipAdress: "192.168.4.10"
property var status: {'status': buzzerConn.state, 'progress': buzzerConn.progress}
onLastTriggeredChanged: {
timer_1.handleToppad()
}
}
Timer {
id: buzzerRefreshTimer
running: buzzerConn.state === "connected"
interval: root.state === "RUNNING" ? 1:1000
repeat: false
onTriggered: {
buzzerConn.refresh()
this.start()
}
}
StartpadConn {
id: startpadConn
ipAdress: "192.168.4.11"
property var status: {'status': startpadConn.state, 'progress': startpadConn.progress}
property string color: root.state === "RUNNING" ? "SET_LED_RUNNING":"SET_LED_STARTING"
onColorChanged: {
appendCommand(color)
}
onLastTriggeredChanged: {
timer_1.handleStartpad()
}
}
Timer {
id: startpadRefreshTimer
running: startpadConn.state === "connected"
interval: root.state === "RUNNING" || root.state === "STARTING" ? 1:1000
repeat: false
onTriggered: {
startpadConn.refresh()
this.start()
}
2018-09-09 20:18:00 +02:00
}
BaseStationConn {
id: baseConn
ipAdress: "localhost"//"raspberrypi.local"
property var status: {'status': baseConn.state, 'progress': baseConn.progress, 'connections': baseConn.connections}
function getTime(type){
var time = parseInt(sendCommand("GET_CURRTIME"))
if(type === "readable"){
return(time / 1000).toFixed(3)
}
else if(type === "raw"){
return(time)
}
}
}
Timer {
id: baseRefreshTimer
running: baseConn.state === "connected"
repeat: false
interval: 1
onTriggered: {
switch(root.state){
case "RUNNING":
var baseReactTime = parseInt(baseConn.sendCommand("GET_REACTTIME"))
console.log(baseReactTime)
if(baseReactTime !== 0){
root.last_run.react_time = baseReactTime
}
timer_1.text = baseConn.getTime("readable") + " sec"
if(baseConn.sendCommand("GET_TIMER_STATE") === "STOPPED"){
root.stop("manual")
}
break
case "STARTING":
var baseReactTime = parseInt(baseConn.sendCommand("GET_REACTTIME"))
if(baseReactTime !== 0){
root.last_run.react_time = baseReactTime
}
var baseNextAction = baseConn.sendCommand("GET_NEXT_ACTION")
if(baseNextAction === "at_marks"){
timer_1.text = qsTr("at your\nmarks")
var baseNextActionDelay = parseFloat(baseConn.sendCommand("GET_NEXT_ACTION_DELAY_PROG"))
prog.progress = baseNextActionDelay * 100
}
else if(baseNextAction === "ready"){
timer_1.text = qsTr("ready")
var baseNextActionDelay = parseFloat(baseConn.sendCommand("GET_NEXT_ACTION_DELAY_PROG"))
prog.progress = baseNextActionDelay * 100
}
else if(baseNextAction === "start"){
timer_1.text = "0.000 sec"
}
var baseState = baseConn.sendCommand("GET_TIMER_STATE")
console.log(baseState)
if(baseState === "RUNNING"){
timer_1.start(1)
root.state = "RUNNING";
}
else if(baseState === "STOPPED"){
var baseReactTime = parseInt(baseConn.sendCommand("GET_REACTTIME"))
if(baseReactTime<0){
root.stop("false")
}
else{
root.stop("cancel")
}
root.state = "STOPPED"
}
break
}
start()
2018-08-29 18:33:39 +02:00
}
}
}
Timer {
id: next_actionTimer
property string action
property double started_at
running: false
repeat: false
onRunningChanged: {
if(!running){
started_at = 0
if(action == "NONE"){
timer_1.text = "0.000 sec"
}
return
}
if(action === "at_marks"){
started_at = new Date().getTime()
timer_1.text = "at your\nmarks"
}
else if(action === "ready"){
started_at = new Date().getTime()
timer_1.text = "ready"
}
}
onTriggered: {
if(action === "at_marks"){
at_marksSound.play()
}
else if(action === "ready"){
action = "NONE"
readySound.play()
}
}
}
Item {
id: sounds
SoundEffect {
id: at_marksSound
source: "qrc:/sounds/at_marks_1.wav"
onPlayingChanged: {
if(!playing && root.state==="STARTING"){
if(_cppAppSettings.loadSetting("ready_en") === "true"){
next_actionTimer.action = "ready"
next_actionTimer.interval = _cppAppSettings.loadSetting("ready_delay")>0 ? _cppAppSettings.loadSetting("ready_delay"):1
next_actionTimer.start()
}
else{
startSound.play()
}
}
}
}
SoundEffect {
id: readySound
source: "qrc:/sounds/ready_1.wav"
onPlayingChanged: {
if(!playing && root.state==="STARTING"){
2018-09-09 20:18:00 +02:00
startSound.play()
}
}
}
SoundEffect {
//start sound
id: startSound
source: "qrc:/sounds/OFFICAL_IFSC_STARTIGNAL.wav"
2018-07-22 16:47:55 +02:00
onPlayingChanged: {
if(!playing && root.state==="STARTING"){
root.state = "RUNNING"
}
else if(playing) {
console.log("start sound started")
timer_1.start(3100)
}
2018-09-09 20:18:00 +02:00
}
}
2018-07-22 16:47:55 +02:00
SoundEffect {
//false-start sound
id: falseSound
source: "qrc:/sounds/false.wav"
}
}
2018-07-22 16:47:55 +02:00
/*------------------------
Timer text an upper line
------------------------*/
2018-07-17 19:17:25 +02:00
Item {
id: time_container
anchors {
top: parent.top
left: parent.left
2018-07-22 16:47:55 +02:00
right: root.landscape() ? startButt.left:parent.right
bottom: root.landscape() ? parent.bottom:startButt.top
bottomMargin: root.landscape() ? undefined:parent.height * 0.1
rightMargin: root.landscape() ? parent.width * 0.05:0
2018-07-17 19:17:25 +02:00
}
Rectangle {
anchors.fill: parent
color: StyleSettings.menuColor
}
SpeedTimer {
id: timer_1
2018-07-17 19:17:25 +02:00
anchors.centerIn: parent
2018-07-22 16:47:55 +02:00
elide: "ElideRight"
color: StyleSettings.textColor
toppadConn: buzzerConn
baseConn: baseConn
startpadConn: startpadConn
text: "0.000 sec"
onStopped: {
root.state = "STOPPED"
}
onStartCanceled: {
root.state = "STOPPED"
next_actionTimer.stop()
at_marksSound.stop()
readySound.stop()
startSound.stop()
if(falseStart && baseConn.state !== "connected"){
falseSound.play()
}
}
2018-07-17 19:17:25 +02:00
}
2018-08-29 18:33:39 +02:00
Label {
id: react_time
property int rtime: root.last_run.react_time
text: qsTr("reaction time (ms): ") + Math.round(rtime)
opacity: (root.state === "RUNNING" || root.sate === "STARTING" || root.state === "STOPPED") && rtime !== 0 ? 1:0
color: StyleSettings.textColor
anchors {
horizontalCenter: parent.horizontalCenter
top: timer_1.bottom
2018-08-29 18:33:39 +02:00
topMargin: parent.height * 0.1
}
Timer {
running: root.state === "RUNNING" || root.sate === "STARTING" || root.state === "STOPPED"
repeat: true
interval: 1
onTriggered: {
react_time.rtime = root.last_run.react_time
}
}
}
2018-07-17 19:17:25 +02:00
}
ConnectionIcon {
id: buzzerLogo
source: "qrc:/graphics/icons/buzzer_black.png"
status: root.connections["buzzer"].status
2018-08-02 12:50:55 +02:00
anchors {
top: parent.top
topMargin: 10
left: parent.left
leftMargin: 10
}
height: root.landscape()? root.height*0.1:root.width*0.1
}
ConnectionIcon {
id: startpadLogo
source: "qrc:/graphics/icons/startpad_black.png"
status: root.connections["startpad"].status
anchors {
top: parent.top
topMargin: 10
left: parent.left
leftMargin: 5 + buzzerLogo.width
}
height: root.landscape()? root.height*0.1:root.width*0.1
2018-08-02 12:50:55 +02:00
}
Rectangle {
id: upper_line
2018-07-22 16:47:55 +02:00
width: root.landscape() ? 1:parent.width
height: root.landscape() ? parent.height:1
color: StyleSettings.lineColor
2018-07-22 16:47:55 +02:00
anchors.left: root.landscape() ? time_container.right:parent.left
anchors.top: root.landscape() ? parent.top:time_container.bottom
anchors.bottom: root.landscape() ? parent.bottom:undefined
2018-07-17 19:17:25 +02:00
}
2018-07-22 16:47:55 +02:00
/*----------------------
Start / Stop / Reset button
2018-07-22 16:47:55 +02:00
----------------------*/
Button {
id : startButt
2018-07-17 19:17:25 +02:00
text: qsTr("start")
2018-07-22 16:47:55 +02:00
property int size: root.landscape() ? parent.width * 0.5:parent.height * 0.5
2018-07-17 19:17:25 +02:00
anchors {
bottom: parent.bottom
bottomMargin: root.height * 0.5 - height * 0.5
2018-07-22 16:47:55 +02:00
right: parent.right
rightMargin: root.width * 0.5 - width * 0.5
}
contentItem: Text {
//make text disappear
2018-07-17 19:17:25 +02:00
}
2018-07-22 16:47:55 +02:00
height: root.landscape() ? size > parent.height * 0.9 ? parent.height * 0.9:size : size
width: root.landscape() ? size : size > parent.width * 0.9 ? parent.width * 0.9:size
2018-07-17 19:17:25 +02:00
background: Rectangle {
color: parent.pressed ? StyleSettings.buttonPressedColor:StyleSettings.buttonColor
border.color: StyleSettings.buttonBorderColor
border.width: 1
radius: width / 2
Label {
id: startButt_text
text: startButt.text
anchors.centerIn: parent
font.pixelSize: parent.height * 0.16
font.family: "Helvetica"
color: enabled ? StyleSettings.textColor:StyleSettings.disabledTextColor
}
2018-07-17 19:17:25 +02:00
}
2018-07-22 16:47:55 +02:00
Behavior on text {
//animate a text change
enabled: true
FadeAnimation {
target: startButt_text
}
}
onClicked: {
switch(root.state) {
case "IDLE":
root.start()
break
case "RUNNING":
root.stop("manual")
break
case "STOPPED":
root.reset()
break
2018-07-22 16:47:55 +02:00
}
}
}
ProgressCircle {
id: prog
anchors.fill: startButt
opacity: baseConn.state !== "connected" ?
next_actionTimer.started_at > 0 ? 1:0
:progress > 0 ? 1:0
lineWidth: 5
property int progress: 0
arcBegin: 0
arcEnd: baseConn.state !== "connected" ? 360 * (( next_actionTimer.interval - ( new Date().getTime() - next_actionTimer.started_at ) ) / next_actionTimer.interval)
:(360/100) * progress
colorCircle: "grey"
animationDuration: baseConn.state === "connected" ? 150:0
Timer {
id: prog_refresh
running: parent.opacity === 1 && baseConn.state !== "connected"
interval: 1
repeat: true
onTriggered: {
prog.arcEnd = 360 * (( next_actionTimer.interval - ( new Date().getTime() - next_actionTimer.started_at ) ) / next_actionTimer.interval)
}
}
2018-07-22 16:47:55 +02:00
}
/*----------------------
Cancel button
----------------------*/
RoundButton {
2018-07-22 16:47:55 +02:00
id: cancelButt
text: qsTr("cancel")
2018-07-22 16:47:55 +02:00
anchors {
right: startButt.right
bottom: startButt.bottom
}
contentItem: Text {
//make text disappear
}
2018-07-22 16:47:55 +02:00
height: startButt.height * 0.3
scale: 0
width: height
enabled: root.state === "STARTING"
2018-07-22 16:47:55 +02:00
onClicked: {
root.stop("cancel")
}
2018-07-22 16:47:55 +02:00
Behavior on scale {
PropertyAnimation {
duration: 200
}
}
background: Rectangle {
color: parent.pressed ? StyleSettings.buttonPressedColor:StyleSettings.buttonColor
border.color: StyleSettings.buttonBorderColor
border.width: 1
radius: width / 2
Label {
id: cancelButt_text
text: cancelButt.text
anchors.centerIn: parent
font.pixelSize: parent.height * 0.16
font.family: "Helvetica"
color: StyleSettings.textColor
}
}
}
/*------
Popups
------*/
SettingsDialog{
2018-08-02 12:50:55 +02:00
id: settingsDialog
connections: root.connections
onConnectRequested: {
switch(type){
case "buzzer":
buzzerConn.connect()
break
case "startpad":
startpadConn.connect()
break
case "baseStation":
baseConn.connectToHost()
break
}
}
2018-07-22 16:47:55 +02:00
}
// ProfilesDialog {
// id: profilesDialog
// }
2018-07-22 16:47:55 +02:00
/*-------------------
lower line and menu
-------------------*/
Rectangle {
width: root.landscape() ? 1:parent.width
height: root.landscape() ? parent.height:1
color: StyleSettings.lineColor
2018-07-22 16:47:55 +02:00
anchors.right: root.landscape() ? menu_container.left:parent.right
anchors.bottom: root.landscape() ? parent.bottom:menu_container.top
anchors.top: root.landscape() ? parent.top:undefined
}
Item {
id: menu_container
anchors {
bottom: parent.bottom
right: parent.right
left: root.landscape() ? startButt.right:parent.left
top: root.landscape() ? parent.top:startButt.bottom
topMargin: root.landscape() ? undefined:parent.height * 0.1
leftMargin: root.landscape() ? parent.width * 0.05:0
}
Rectangle {
anchors.fill: parent
color: StyleSettings.menuColor
}
RoundButton {
2018-07-22 16:47:55 +02:00
id: settingsButt
anchors {
//center
verticalCenter: root.landscape() ? undefined:parent.verticalCenter
horizontalCenter: root.landscape() ? parent.horizontalCenter:undefined
//set anchors
left: root.landscape() ? undefined:parent.left
top: root.landscape() ? parent.top:undefined
//align in landscape mode
2018-08-11 23:54:34 +02:00
//for two buttons: topMargin: root.landscape() ? (parent.height - (height * 2)) / 3:undefined
topMargin: root.landscape() ? (parent.height * 0.5 - (height * 0.5)):undefined
2018-07-22 16:47:55 +02:00
//align in portrait mode
2018-08-11 23:54:34 +02:00
//for two buttons: leftMargin: root.landscape() ? undefined:(parent.width - width * 2) / 3
leftMargin: root.landscape() ? undefined:(parent.width * 0.5 - width * 0.5)
2018-07-22 16:47:55 +02:00
}
2018-07-22 16:47:55 +02:00
height: root.landscape() ? parent.width * 0.7:parent.height * 0.7
width: height
onClicked: {
settingsDialog.open()
2018-07-22 16:47:55 +02:00
}
background: Rectangle {
color: parent.pressed ? StyleSettings.buttonPressedColor:StyleSettings.buttonColor
border.color: StyleSettings.buttonBorderColor
border.width: 1
radius: width / 2
Image {
id: settungsButt_Image
source: StyleSettings.settIcon
anchors.centerIn: parent
height: parent.height * 0.7
width: parent.width * 0.7
mipmap: true
2018-07-22 16:47:55 +02:00
}
}
}
2018-08-11 23:54:34 +02:00
/*
RoundButton {
2018-07-22 16:47:55 +02:00
id: profilesButt
anchors {
verticalCenter: root.landscape() ? undefined:parent.verticalCenter
horizontalCenter: root.landscape() ? parent.horizontalCenter:undefined
left: root.landscape() ? undefined:settingsButt.right
top: root.landscape() ? settingsButt.bottom:undefined
topMargin: root.landscape() ? (parent.height - (height * 2)) / 3:undefined
leftMargin: root.landscape() ? undefined:(parent.width - width * 2) / 3
}
height: root.landscape() ? parent.width * 0.7:parent.height * 0.7
2018-07-22 16:47:55 +02:00
width: height
onPressedChanged: {
if(pressed){
background.color = "lightgrey"
}
else {
background.color = "white"
}
2018-07-22 16:47:55 +02:00
}
onClicked: {
2018-08-02 12:50:55 +02:00
profilesDialog.open()
2018-07-22 16:47:55 +02:00
}
2018-07-17 19:17:25 +02:00
background: Rectangle {
color: "white"
border.color: "grey"
border.width: 1
radius: width / 2
Image {
id: profilesButt_Image
source: "qrc:/graphics/icons/user.png"
anchors.centerIn: parent
height: parent.height * 0.5
width: parent.width * 0.5
mipmap: true
2018-07-22 16:47:55 +02:00
}
2018-07-17 19:17:25 +02:00
}
}
2018-08-11 23:54:34 +02:00
*/
2018-07-17 19:17:25 +02:00
}
2018-07-22 16:47:55 +02:00
/*----------------------
Timer states
----------------------*/
2018-07-17 19:17:25 +02:00
states: [
2018-07-22 16:47:55 +02:00
State {
name: "IDLE"
//state for the start page
PropertyChanges { target: timer_1; pixelSize: root.landscape() ? parent.width * 0.1:parent.height * 0.3; scale: 1 }
2018-07-22 16:47:55 +02:00
PropertyChanges {
target: time_container;
anchors.bottomMargin: root.landscape() ? undefined:parent.height * 0.1;
anchors.rightMargin: root.landscape() ? parent.height * 0.05:0
}
PropertyChanges {
target: startButt;
enabled: true; text: qsTr("start");
2018-07-22 16:47:55 +02:00
size: root.landscape() ? parent.width * 0.5:parent.height * 0.5
anchors.bottomMargin: parent.height * 0.5 - startButt.height * 0.5
anchors.rightMargin: parent.width * 0.5 - startButt.width * 0.5
}
},
State {
name: "STARTING"
//state for the start sequence
PropertyChanges { target: startButt; enabled: false; text: qsTr("starting...");
anchors.rightMargin: root.landscape() ? parent.width * 0.05:parent.width * 0.5 - startButt.width * 0.5 //put the button more to the right to hide the menu (only in landscape mode)
anchors.bottomMargin: root.landscape() ? parent.height * 0.5 - startButt.height * 0.5:parent.height * 0.1 //put the button lower to hide the menu (only in portrait mode)
2018-07-22 16:47:55 +02:00
}
PropertyChanges { target: timer_1; pixelSize: root.landscape() ? parent.width * 0.2:parent.height * 0.3; scale: 1 }
2018-07-22 16:47:55 +02:00
PropertyChanges { target: cancelButt; scale: 1}
PropertyChanges { target: menu_container; }
},
State {
name: "RUNNING"
//state when the timer is running
PropertyChanges { target: timer_1; pixelSize: root.landscape() ? parent.width * 0.2:parent.height * 0.3; scale: 1 }
2018-07-22 16:47:55 +02:00
PropertyChanges { target: startButt; enabled: true;
text: "stop"
anchors.rightMargin: root.landscape() ? parent.width * 0.05:parent.width * 0.5 - startButt.width * 0.5 //put the button more to the right to hide the menu (only in landscape mode)
anchors.bottomMargin: root.landscape() ? parent.height * 0.5 - startButt.height * 0.5:parent.height * 0.1 //put the button lower to hide the menu (only in portrait mode)
2018-07-22 16:47:55 +02:00
}
},
State {
name: "STOPPED"
//state when the meassuring is over
PropertyChanges {
target: timer_1;
pixelSize: root.landscape() ? parent.width * 0.15:parent.height * 0.1;
2018-07-22 16:47:55 +02:00
scale: 1
}
PropertyChanges {
target: startButt;
enabled: true; text: qsTr("reset");
2018-07-22 16:47:55 +02:00
size: root.landscape() ? parent.height * 0.35:parent.height * 0.2;
anchors.bottomMargin: root.landscape() ? parent.height * 0.5 - startButt.height * 0.5:parent.height * 0.2 - startButt.height * 0.5
anchors.rightMargin: root.landscape() ? parent.height * 0.2 - startButt.height * 0.5:parent.width * 0.5 - startButt.width * 0.5
}
PropertyChanges {
target: time_container;
anchors.rightMargin: root.landscape() ? 0-startButt.width/2:undefined
anchors.bottomMargin: root.landscape() ? undefined:0-startButt.height/2
}
}
]
/*----------------------
Timer animations
----------------------*/
2018-07-17 19:17:25 +02:00
transitions: [
Transition {
NumberAnimation { properties: "size,rightMargin,height,width,bottomMargin,font.pixelSize,pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
2018-07-17 19:17:25 +02:00
},
Transition {
to: "STOPPED"
NumberAnimation { properties: "size,rightMargin,height,width,bottomMargin,font.pixelSize,pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
},
Transition {
to: "IDLE"
NumberAnimation { properties: "size,rightMargin,height,width,bottomMargin,font.pixelSize,pixelSize"; easing.type: Easing.InOutQuad; duration: 700 }
},
Transition {
to: "RUNNING"
//disable transitions for the RUNNING state
2018-07-17 19:17:25 +02:00
}
]
2018-07-22 16:47:55 +02:00
/*----------------------
Timer functions
----------------------*/
function landscape(){
return(root.height < root.width)
}
/*----Functions to control the stopwatch----*/
function start(){
if(baseConn.state === "connected"){
var ret = baseConn.sendCommand("CMD_START_TIMER")
if(ret === "OK"){
root.state = "STARTING"
timer_1.setStarting()
return
}
}
root.state = "STARTING"
timer_1.setStarting()
if(_cppAppSettings.loadSetting("at_marks_en") === "true"){
next_actionTimer.action = "at_marks"
next_actionTimer.interval = _cppAppSettings.loadSetting("at_marks_delay")>0 ? _cppAppSettings.loadSetting("at_marks_delay"):1
next_actionTimer.start()
return
}
if(_cppAppSettings.loadSetting("ready_en") === "true"){
next_actionTimer.action = "ready"
next_actionTimer.interval = _cppAppSettings.loadSetting("ready_delay")>0 ? _cppAppSettings.loadSetting("ready_delay"):1
next_actionTimer.start()
return
}
startSound.play()
}
function stop(type){
if(type === "manual" || type === "cancel"){
if(baseConn.state === "connected"){
baseConn.sendCommand("CMD_STOP_TIMER")
}
}
root.state = "STOPPED"
timer_1.stop(type)
}
function reset(){
if(baseConn.state === "connected"){
baseConn.sendCommand("CMD_RESET_TIMER")
}
timer_1.reset()
root.state = "IDLE"
}
2018-07-17 19:17:25 +02:00
}
}