Browse Source

Umm, yeah

curiousmuch 6 years ago
parent
commit
0cd47745bb
11 changed files with 1124 additions and 95 deletions
  1. 11 5
      README.md
  2. BIN
      icon/devicetype/modulev3c.png
  3. 145 57
      pyqt/mainwindow.py
  4. 360 0
      qt/actuator.ui
  5. 283 0
      qt/actuator.ui.autosave
  6. 38 7
      qt/mainwindow.ui
  7. 4 0
      wsc-tools-clear-topics.sh
  8. 9 2
      wsc_client.py
  9. 82 9
      wsc_device_encoder.py
  10. 115 10
      wsc_tools.py
  11. 77 5
      wsc_ui.py

+ 11 - 5
README.md

@@ -6,12 +6,18 @@ The purpose of this application is to provide an interface for therapists and ca
     + Paho-MQTT (Network Communication)
     + PySerial  (Configuration of IdeasX device over USB)
 
-The software is split into a few different files for sanity. 
+The Paho-MQTT mqttc module was modified (in a total hack-ish method). The mqttc does not provde the option to set a timeout for opening a socket. Therefore, if the user enters an incorrect IP / Port, the backend code (wsc_client.py) will hang in-definitly. Therefore, the following line was addded to client.py 
+``` 
+socket.setDefaultTimeout(1)
+```
 
-**wsc_client.py** contains majority of the networking code for handling ideasX devices. 
+## List of Brokers 
+The following link lists a number of publically available - free - brokers to support IdeasX. 
+[https://github.com/mqtt/mqtt.github.io/wiki/public_brokers]
 
-**wsc_device.py** contains classes for each device in the IdeasX system to decode data recieved by the wsc\_client.py networking thread and commands which can be send to each device. 
+## Encoder Details / Notes 
 
-**wsc_tools.py** contains helper tools created wsc\_io.py file (normally dumb things like converting a byte value from the wsc\_client into a percentage etc.)
+### Switches 
+
+0b110000000000001101100000001 10100
 
-**wsc_ui.py** contains the UI for the appication

BIN
icon/devicetype/modulev3c.png


+ 145 - 57
pyqt/mainwindow.py

@@ -1,8 +1,8 @@
 # -*- coding: utf-8 -*-
 
-# Form implementation generated from reading ui file 'mainwindow.ui'
+# Form implementation generated from reading ui file './qt/mainwindow.ui'
 #
-# Created by: PyQt5 UI code generator 5.7
+# Created by: PyQt5 UI code generator 5.6
 #
 # WARNING! All changes made in this file will be lost!
 
@@ -11,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
 class Ui_MainWindow(object):
     def setupUi(self, MainWindow):
         MainWindow.setObjectName("MainWindow")
-        MainWindow.resize(525, 648)
+        MainWindow.resize(525, 753)
         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
         sizePolicy.setHorizontalStretch(0)
         sizePolicy.setVerticalStretch(0)
@@ -60,7 +60,7 @@ class Ui_MainWindow(object):
         self.scrollEncoder.setWidgetResizable(True)
         self.scrollEncoder.setObjectName("scrollEncoder")
         self.contentEncoder = QtWidgets.QWidget()
-        self.contentEncoder.setGeometry(QtCore.QRect(0, 0, 459, 556))
+        self.contentEncoder.setGeometry(QtCore.QRect(0, 0, 456, 658))
         self.contentEncoder.setObjectName("contentEncoder")
         self.scrollEncoder.setWidget(self.contentEncoder)
         self.gridLayout.addWidget(self.scrollEncoder, 0, 0, 1, 3)
@@ -91,59 +91,125 @@ class Ui_MainWindow(object):
         self.tabWidget.addTab(self.tabActuator, "")
         self.tabSetting = QtWidgets.QWidget()
         self.tabSetting.setObjectName("tabSetting")
-        self.gridLayout_4 = QtWidgets.QGridLayout(self.tabSetting)
-        self.gridLayout_4.setContentsMargins(9, 9, 9, 9)
-        self.gridLayout_4.setObjectName("gridLayout_4")
-        self.labelPassword = QtWidgets.QLabel(self.tabSetting)
-        self.labelPassword.setObjectName("labelPassword")
-        self.gridLayout_4.addWidget(self.labelPassword, 9, 0, 1, 1)
-        self.wifiPassword = QtWidgets.QLineEdit(self.tabSetting)
-        self.wifiPassword.setObjectName("wifiPassword")
-        self.gridLayout_4.addWidget(self.wifiPassword, 9, 1, 1, 2)
-        self.labelNetworkBroker = QtWidgets.QLabel(self.tabSetting)
+        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tabSetting)
+        self.verticalLayout_2.setContentsMargins(9, 9, 9, 9)
+        self.verticalLayout_2.setObjectName("verticalLayout_2")
+        self.groupNetwork = QtWidgets.QGroupBox(self.tabSetting)
+        self.groupNetwork.setObjectName("groupNetwork")
+        self.formLayout = QtWidgets.QFormLayout(self.groupNetwork)
+        self.formLayout.setObjectName("formLayout")
+        self.labelNetworkBroker = QtWidgets.QLabel(self.groupNetwork)
         self.labelNetworkBroker.setObjectName("labelNetworkBroker")
-        self.gridLayout_4.addWidget(self.labelNetworkBroker, 1, 0, 1, 1)
-        self.localBroker = QtWidgets.QLineEdit(self.tabSetting)
-        self.localBroker.setObjectName("localBroker")
-        self.gridLayout_4.addWidget(self.localBroker, 3, 1, 1, 2)
-        self.networkBroker = QtWidgets.QLineEdit(self.tabSetting)
+        self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.labelNetworkBroker)
+        self.networkBroker = QtWidgets.QLineEdit(self.groupNetwork)
+        self.networkBroker.setInputMethodHints(QtCore.Qt.ImhUrlCharactersOnly)
         self.networkBroker.setObjectName("networkBroker")
-        self.gridLayout_4.addWidget(self.networkBroker, 1, 1, 1, 2)
-        self.labelSSID = QtWidgets.QLabel(self.tabSetting)
-        self.labelSSID.setObjectName("labelSSID")
-        self.gridLayout_4.addWidget(self.labelSSID, 8, 0, 1, 1)
-        self.localPort = QtWidgets.QLineEdit(self.tabSetting)
-        self.localPort.setObjectName("localPort")
-        self.gridLayout_4.addWidget(self.localPort, 4, 1, 1, 2)
-        self.wifiSSID = QtWidgets.QLineEdit(self.tabSetting)
-        self.wifiSSID.setObjectName("wifiSSID")
-        self.gridLayout_4.addWidget(self.wifiSSID, 8, 1, 1, 2)
-        self.labelAPSelector = QtWidgets.QLabel(self.tabSetting)
-        self.labelAPSelector.setObjectName("labelAPSelector")
-        self.gridLayout_4.addWidget(self.labelAPSelector, 7, 0, 1, 1)
-        self.networkPort = QtWidgets.QLineEdit(self.tabSetting)
+        self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.networkBroker)
+        self.networkPort = QtWidgets.QLineEdit(self.groupNetwork)
+        self.networkPort.setInputMethodHints(QtCore.Qt.ImhPreferNumbers)
         self.networkPort.setText("")
         self.networkPort.setObjectName("networkPort")
-        self.gridLayout_4.addWidget(self.networkPort, 2, 1, 1, 2)
-        self.selectAP = QtWidgets.QSpinBox(self.tabSetting)
+        self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.networkPort)
+        self.labelLocalBroker = QtWidgets.QLabel(self.groupNetwork)
+        self.labelLocalBroker.setObjectName("labelLocalBroker")
+        self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.labelLocalBroker)
+        self.localBroker = QtWidgets.QLineEdit(self.groupNetwork)
+        self.localBroker.setInputMethodHints(QtCore.Qt.ImhUrlCharactersOnly)
+        self.localBroker.setObjectName("localBroker")
+        self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.localBroker)
+        self.localPort = QtWidgets.QLineEdit(self.groupNetwork)
+        self.localPort.setInputMethodHints(QtCore.Qt.ImhPreferNumbers)
+        self.localPort.setObjectName("localPort")
+        self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.localPort)
+        self.labelOTA = QtWidgets.QLabel(self.groupNetwork)
+        self.labelOTA.setObjectName("labelOTA")
+        self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.labelOTA)
+        self.otaServer = QtWidgets.QLineEdit(self.groupNetwork)
+        self.otaServer.setText("")
+        self.otaServer.setObjectName("otaServer")
+        self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.otaServer)
+        self.lineEdit_2 = QtWidgets.QLineEdit(self.groupNetwork)
+        self.lineEdit_2.setObjectName("lineEdit_2")
+        self.formLayout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.lineEdit_2)
+        self.verticalLayout_2.addWidget(self.groupNetwork)
+        self.buttonBoxNetwork = QtWidgets.QDialogButtonBox(self.tabSetting)
+        self.buttonBoxNetwork.setStandardButtons(QtWidgets.QDialogButtonBox.Apply|QtWidgets.QDialogButtonBox.Cancel)
+        self.buttonBoxNetwork.setObjectName("buttonBoxNetwork")
+        self.verticalLayout_2.addWidget(self.buttonBoxNetwork)
+        self.groupDeviceSettings = QtWidgets.QGroupBox(self.tabSetting)
+        self.groupDeviceSettings.setObjectName("groupDeviceSettings")
+        self.formLayout_2 = QtWidgets.QFormLayout(self.groupDeviceSettings)
+        self.formLayout_2.setObjectName("formLayout_2")
+        self.labelAPSelector = QtWidgets.QLabel(self.groupDeviceSettings)
+        self.labelAPSelector.setObjectName("labelAPSelector")
+        self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.labelAPSelector)
+        self.selectAP = QtWidgets.QSpinBox(self.groupDeviceSettings)
         self.selectAP.setSuffix("")
         self.selectAP.setMinimum(1)
         self.selectAP.setMaximum(5)
         self.selectAP.setObjectName("selectAP")
-        self.gridLayout_4.addWidget(self.selectAP, 7, 1, 1, 1)
+        self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.selectAP)
+        self.labelSSID = QtWidgets.QLabel(self.groupDeviceSettings)
+        self.labelSSID.setObjectName("labelSSID")
+        self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.labelSSID)
+        self.wifiSSID = QtWidgets.QLineEdit(self.groupDeviceSettings)
+        self.wifiSSID.setClearButtonEnabled(True)
+        self.wifiSSID.setObjectName("wifiSSID")
+        self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.wifiSSID)
+        self.labelPassword = QtWidgets.QLabel(self.groupDeviceSettings)
+        self.labelPassword.setObjectName("labelPassword")
+        self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.labelPassword)
+        self.wifiPassword = QtWidgets.QLineEdit(self.groupDeviceSettings)
+        self.wifiPassword.setClearButtonEnabled(True)
+        self.wifiPassword.setObjectName("wifiPassword")
+        self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.wifiPassword)
+        self.labelPort = QtWidgets.QLabel(self.groupDeviceSettings)
+        self.labelPort.setObjectName("labelPort")
+        self.formLayout_2.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.labelPort)
+        self.selectPort = QtWidgets.QComboBox(self.groupDeviceSettings)
+        self.selectPort.setObjectName("selectPort")
+        self.formLayout_2.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.selectPort)
+        self.buttonRefreshPorts = QtWidgets.QToolButton(self.groupDeviceSettings)
+        self.buttonRefreshPorts.setLayoutDirection(QtCore.Qt.LeftToRight)
+        self.buttonRefreshPorts.setObjectName("buttonRefreshPorts")
+        self.formLayout_2.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.buttonRefreshPorts)
+        self.verticalLayout_2.addWidget(self.groupDeviceSettings)
+        self.buttonBoxDevice = QtWidgets.QDialogButtonBox(self.tabSetting)
+        self.buttonBoxDevice.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Save)
+        self.buttonBoxDevice.setCenterButtons(False)
+        self.buttonBoxDevice.setObjectName("buttonBoxDevice")
+        self.verticalLayout_2.addWidget(self.buttonBoxDevice)
+        self.groupUpdat = QtWidgets.QGroupBox(self.tabSetting)
+        self.groupUpdat.setObjectName("groupUpdat")
+        self.formLayout_3 = QtWidgets.QFormLayout(self.groupUpdat)
+        self.formLayout_3.setObjectName("formLayout_3")
+        self.lineEdit = QtWidgets.QLineEdit(self.groupUpdat)
+        self.lineEdit.setObjectName("lineEdit")
+        self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.lineEdit)
+        self.pushButton = QtWidgets.QPushButton(self.groupUpdat)
+        self.pushButton.setObjectName("pushButton")
+        self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton)
+        self.label = QtWidgets.QLabel(self.groupUpdat)
+        self.label.setObjectName("label")
+        self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)
+        self.verticalLayout_2.addWidget(self.groupUpdat)
+        self.groupBox_2 = QtWidgets.QGroupBox(self.tabSetting)
+        self.groupBox_2.setObjectName("groupBox_2")
+        self.formLayout_4 = QtWidgets.QFormLayout(self.groupBox_2)
+        self.formLayout_4.setObjectName("formLayout_4")
+        self.label_2 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_2.setObjectName("label_2")
+        self.formLayout_4.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_2)
+        self.comboBox = QtWidgets.QComboBox(self.groupBox_2)
+        self.comboBox.setCurrentText("")
+        self.comboBox.setObjectName("comboBox")
+        self.formLayout_4.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.comboBox)
+        self.pushButton_2 = QtWidgets.QPushButton(self.groupBox_2)
+        self.pushButton_2.setObjectName("pushButton_2")
+        self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton_2)
+        self.verticalLayout_2.addWidget(self.groupBox_2)
         spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
-        self.gridLayout_4.addItem(spacerItem2, 6, 0, 1, 2)
-        self.labelLocalBroker = QtWidgets.QLabel(self.tabSetting)
-        self.labelLocalBroker.setObjectName("labelLocalBroker")
-        self.gridLayout_4.addWidget(self.labelLocalBroker, 3, 0, 1, 1)
-        spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
-        self.gridLayout_4.addItem(spacerItem3, 7, 2, 1, 1)
-        self.buttonTrainDevice = QtWidgets.QPushButton(self.tabSetting)
-        self.buttonTrainDevice.setObjectName("buttonTrainDevice")
-        self.gridLayout_4.addWidget(self.buttonTrainDevice, 10, 2, 1, 1)
-        self.buttonSettings = QtWidgets.QPushButton(self.tabSetting)
-        self.buttonSettings.setObjectName("buttonSettings")
-        self.gridLayout_4.addWidget(self.buttonSettings, 5, 2, 1, 1)
+        self.verticalLayout_2.addItem(spacerItem2)
         self.tabWidget.addTab(self.tabSetting, "")
         self.verticalLayout.addWidget(self.tabWidget)
         MainWindow.setCentralWidget(self.centralwidget)
@@ -155,7 +221,7 @@ class Ui_MainWindow(object):
         MainWindow.setStatusBar(self.statusbar)
 
         self.retranslateUi(MainWindow)
-        self.tabWidget.setCurrentIndex(0)
+        self.tabWidget.setCurrentIndex(2)
         QtCore.QMetaObject.connectSlotsByName(MainWindow)
 
     def retranslateUi(self, MainWindow):
@@ -165,17 +231,39 @@ class Ui_MainWindow(object):
         self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabEncoder), _translate("MainWindow", "Encoders"))
         self.searchActuator.setPlaceholderText(_translate("MainWindow", "Search for Actuator"))
         self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabActuator), _translate("MainWindow", "Actuators"))
-        self.labelPassword.setText(_translate("MainWindow", "Password:"))
+        self.groupNetwork.setTitle(_translate("MainWindow", "Network Settings"))
         self.labelNetworkBroker.setText(_translate("MainWindow", "Network Broker:"))
-        self.localBroker.setPlaceholderText(_translate("MainWindow", "URL or IP"))
         self.networkBroker.setPlaceholderText(_translate("MainWindow", "URL or IP"))
-        self.labelSSID.setText(_translate("MainWindow", "SSID:"))
-        self.localPort.setPlaceholderText(_translate("MainWindow", "Port"))
-        self.labelAPSelector.setText(_translate("MainWindow", "Wi-Fi Access Point:"))
         self.networkPort.setPlaceholderText(_translate("MainWindow", "Port"))
-        self.selectAP.setPrefix(_translate("MainWindow", "AP "))
         self.labelLocalBroker.setText(_translate("MainWindow", "Local Broker:"))
-        self.buttonTrainDevice.setText(_translate("MainWindow", "Train IdeasX Device"))
-        self.buttonSettings.setText(_translate("MainWindow", "Apply Settings"))
+        self.localBroker.setPlaceholderText(_translate("MainWindow", "URL or IP"))
+        self.localPort.setPlaceholderText(_translate("MainWindow", "Port"))
+        self.labelOTA.setText(_translate("MainWindow", "OTA Server:"))
+        self.otaServer.setPlaceholderText(_translate("MainWindow", "URL or IP"))
+        self.lineEdit_2.setPlaceholderText(_translate("MainWindow", "Port"))
+        self.groupDeviceSettings.setTitle(_translate("MainWindow", "Device Settings"))
+        self.labelAPSelector.setText(_translate("MainWindow", "Wi-Fi Access Point:"))
+        self.selectAP.setPrefix(_translate("MainWindow", "Access Point "))
+        self.labelSSID.setText(_translate("MainWindow", "SSID:"))
+        self.labelPassword.setText(_translate("MainWindow", "Password:"))
+        self.labelPort.setText(_translate("MainWindow", "Port:"))
+        self.buttonRefreshPorts.setText(_translate("MainWindow", "Refresh Ports"))
+        self.groupUpdat.setTitle(_translate("MainWindow", "WSC Update Settings"))
+        self.lineEdit.setPlaceholderText(_translate("MainWindow", "URL to GitHub Repository"))
+        self.pushButton.setText(_translate("MainWindow", "Check for Update"))
+        self.label.setText(_translate("MainWindow", "WSC Software Repository:"))
+        self.groupBox_2.setTitle(_translate("MainWindow", "Global Commands"))
+        self.label_2.setText(_translate("MainWindow", "Global Command: "))
+        self.pushButton_2.setText(_translate("MainWindow", "Send Command"))
         self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabSetting), _translate("MainWindow", "Settings"))
 
+
+if __name__ == "__main__":
+    import sys
+    app = QtWidgets.QApplication(sys.argv)
+    MainWindow = QtWidgets.QMainWindow()
+    ui = Ui_MainWindow()
+    ui.setupUi(MainWindow)
+    MainWindow.show()
+    sys.exit(app.exec_())
+

+ 360 - 0
qt/actuator.ui

@@ -0,0 +1,360 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>IdeasXDevice</class>
+ <widget class="QWidget" name="IdeasXDevice">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>522</width>
+    <height>84</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>452</width>
+    <height>84</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>16777215</width>
+    <height>84</height>
+   </size>
+  </property>
+  <property name="acceptDrops">
+   <bool>false</bool>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <property name="autoFillBackground">
+   <bool>false</bool>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <property name="spacing">
+    <number>0</number>
+   </property>
+   <property name="leftMargin">
+    <number>9</number>
+   </property>
+   <property name="topMargin">
+    <number>3</number>
+   </property>
+   <property name="rightMargin">
+    <number>9</number>
+   </property>
+   <property name="bottomMargin">
+    <number>3</number>
+   </property>
+   <item>
+    <widget class="QWidget" name="widgetInfo" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Ignored">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>290</width>
+       <height>70</height>
+      </size>
+     </property>
+     <widget class="QLabel" name="labelDeviceType">
+      <property name="geometry">
+       <rect>
+        <x>5</x>
+        <y>0</y>
+        <width>101</width>
+        <height>76</height>
+       </rect>
+      </property>
+      <property name="toolTip">
+       <string extracomment="RSSI: -20 dBm"/>
+      </property>
+      <property name="statusTip">
+       <string/>
+      </property>
+      <property name="pixmap">
+       <pixmap>../icon/devicetype/modulev3b.png</pixmap>
+      </property>
+      <property name="scaledContents">
+       <bool>false</bool>
+      </property>
+     </widget>
+     <widget class="QLabel" name="labelModuleID">
+      <property name="geometry">
+       <rect>
+        <x>119</x>
+        <y>5</y>
+        <width>311</width>
+        <height>36</height>
+       </rect>
+      </property>
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="font">
+       <font>
+        <family>TakaoPGothic</family>
+        <pointsize>20</pointsize>
+        <weight>50</weight>
+        <italic>false</italic>
+        <bold>false</bold>
+       </font>
+      </property>
+      <property name="frameShape">
+       <enum>QFrame::NoFrame</enum>
+      </property>
+      <property name="text">
+       <string>Sarah Stanton</string>
+      </property>
+      <property name="scaledContents">
+       <bool>true</bool>
+      </property>
+     </widget>
+     <widget class="QLabel" name="labelSignal">
+      <property name="geometry">
+       <rect>
+        <x>117</x>
+        <y>43</y>
+        <width>31</width>
+        <height>31</height>
+       </rect>
+      </property>
+      <property name="toolTip">
+       <string extracomment="-40 dBm"/>
+      </property>
+      <property name="pixmap">
+       <pixmap>../icon/network/network-wireless-signal-ok-symbolic.png</pixmap>
+      </property>
+      <property name="scaledContents">
+       <bool>false</bool>
+      </property>
+     </widget>
+     <widget class="QLabel" name="labelBattery">
+      <property name="geometry">
+       <rect>
+        <x>157</x>
+        <y>43</y>
+        <width>31</width>
+        <height>31</height>
+       </rect>
+      </property>
+      <property name="toolTip">
+       <string extracomment="50%"/>
+      </property>
+      <property name="statusTip">
+       <string extracomment="50%"/>
+      </property>
+      <property name="pixmap">
+       <pixmap>../icon/battery/battery-good-charging-symbolic.png</pixmap>
+      </property>
+      <property name="scaledContents">
+       <bool>false</bool>
+      </property>
+     </widget>
+     <widget class="QToolButton" name="buttonSwitchOne">
+      <property name="geometry">
+       <rect>
+        <x>197</x>
+        <y>43</y>
+        <width>25</width>
+        <height>31</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>...</string>
+      </property>
+      <property name="icon">
+       <iconset>
+        <normaloff>../icon/switch/switch-one-enabled.png</normaloff>../icon/switch/switch-one-enabled.png</iconset>
+      </property>
+      <property name="iconSize">
+       <size>
+        <width>30</width>
+        <height>30</height>
+       </size>
+      </property>
+      <property name="autoRaise">
+       <bool>true</bool>
+      </property>
+     </widget>
+     <widget class="QToolButton" name="buttonSwitchTwo">
+      <property name="geometry">
+       <rect>
+        <x>219</x>
+        <y>43</y>
+        <width>21</width>
+        <height>31</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>...</string>
+      </property>
+      <property name="icon">
+       <iconset>
+        <normaloff>../icon/switch/switch-two-enabled.png</normaloff>../icon/switch/switch-two-enabled.png</iconset>
+      </property>
+      <property name="iconSize">
+       <size>
+        <width>30</width>
+        <height>30</height>
+       </size>
+      </property>
+      <property name="autoRaise">
+       <bool>true</bool>
+      </property>
+     </widget>
+     <widget class="QToolButton" name="buttonSwitchAdaptive">
+      <property name="geometry">
+       <rect>
+        <x>238</x>
+        <y>43</y>
+        <width>25</width>
+        <height>31</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>...</string>
+      </property>
+      <property name="icon">
+       <iconset>
+        <normaloff>../icon/switch/switch-adaptive-disabled.png</normaloff>../icon/switch/switch-adaptive-disabled.png</iconset>
+      </property>
+      <property name="iconSize">
+       <size>
+        <width>30</width>
+        <height>30</height>
+       </size>
+      </property>
+      <property name="autoRaise">
+       <bool>true</bool>
+      </property>
+     </widget>
+     <widget class="QLabel" name="labelOTA">
+      <property name="geometry">
+       <rect>
+        <x>270</x>
+        <y>45</y>
+        <width>36</width>
+        <height>26</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string/>
+      </property>
+      <property name="pixmap">
+       <pixmap>../icon/ota/updating.png</pixmap>
+      </property>
+     </widget>
+     <zorder>labelDeviceType</zorder>
+     <zorder>labelSignal</zorder>
+     <zorder>labelBattery</zorder>
+     <zorder>buttonSwitchOne</zorder>
+     <zorder>buttonSwitchTwo</zorder>
+     <zorder>buttonSwitchAdaptive</zorder>
+     <zorder>labelModuleID</zorder>
+     <zorder>labelOTA</zorder>
+    </widget>
+   </item>
+   <item>
+    <widget class="QWidget" name="widgetControls" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>75</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>66</height>
+      </size>
+     </property>
+     <widget class="QLabel" name="labelStatus">
+      <property name="geometry">
+       <rect>
+        <x>-26</x>
+        <y>44</y>
+        <width>181</width>
+        <height>31</height>
+       </rect>
+      </property>
+      <property name="font">
+       <font>
+        <pointsize>10</pointsize>
+        <italic>true</italic>
+       </font>
+      </property>
+      <property name="text">
+       <string>Last Update: 2:30AM</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+      </property>
+     </widget>
+     <widget class="QToolButton" name="buttonMenu">
+      <property name="geometry">
+       <rect>
+        <x>131</x>
+        <y>10</y>
+        <width>21</width>
+        <height>36</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Activate </string>
+      </property>
+      <property name="autoRaise">
+       <bool>false</bool>
+      </property>
+      <property name="arrowType">
+       <enum>Qt::DownArrow</enum>
+      </property>
+     </widget>
+     <widget class="QToolButton" name="buttonActivate">
+      <property name="geometry">
+       <rect>
+        <x>41</x>
+        <y>10</y>
+        <width>91</width>
+        <height>36</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Activate</string>
+      </property>
+      <property name="checkable">
+       <bool>true</bool>
+      </property>
+      <property name="popupMode">
+       <enum>QToolButton::DelayedPopup</enum>
+      </property>
+      <property name="toolButtonStyle">
+       <enum>Qt::ToolButtonTextOnly</enum>
+      </property>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 283 - 0
qt/actuator.ui.autosave

@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>IdeasXDevice</class>
+ <widget class="QWidget" name="IdeasXDevice">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>522</width>
+    <height>84</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>452</width>
+    <height>84</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>16777215</width>
+    <height>84</height>
+   </size>
+  </property>
+  <property name="acceptDrops">
+   <bool>false</bool>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <property name="autoFillBackground">
+   <bool>false</bool>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <property name="spacing">
+    <number>0</number>
+   </property>
+   <property name="leftMargin">
+    <number>9</number>
+   </property>
+   <property name="topMargin">
+    <number>3</number>
+   </property>
+   <property name="rightMargin">
+    <number>9</number>
+   </property>
+   <property name="bottomMargin">
+    <number>3</number>
+   </property>
+   <item>
+    <widget class="QWidget" name="widgetInfo" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Ignored">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>290</width>
+       <height>70</height>
+      </size>
+     </property>
+     <widget class="QLabel" name="labelDeviceType">
+      <property name="geometry">
+       <rect>
+        <x>5</x>
+        <y>0</y>
+        <width>101</width>
+        <height>76</height>
+       </rect>
+      </property>
+      <property name="toolTip">
+       <string extracomment="RSSI: -20 dBm"/>
+      </property>
+      <property name="statusTip">
+       <string/>
+      </property>
+      <property name="pixmap">
+       <pixmap>../icon/devicetype/modulev3c.png</pixmap>
+      </property>
+      <property name="scaledContents">
+       <bool>false</bool>
+      </property>
+     </widget>
+     <widget class="QLabel" name="labelDeviceID">
+      <property name="geometry">
+       <rect>
+        <x>119</x>
+        <y>5</y>
+        <width>311</width>
+        <height>36</height>
+       </rect>
+      </property>
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="font">
+       <font>
+        <family>TakaoPGothic</family>
+        <pointsize>20</pointsize>
+        <weight>50</weight>
+        <italic>false</italic>
+        <bold>false</bold>
+       </font>
+      </property>
+      <property name="frameShape">
+       <enum>QFrame::NoFrame</enum>
+      </property>
+      <property name="text">
+       <string>Sarah Stanton</string>
+      </property>
+      <property name="scaledContents">
+       <bool>true</bool>
+      </property>
+     </widget>
+     <widget class="QLabel" name="labelSignal">
+      <property name="geometry">
+       <rect>
+        <x>117</x>
+        <y>43</y>
+        <width>31</width>
+        <height>31</height>
+       </rect>
+      </property>
+      <property name="toolTip">
+       <string extracomment="-40 dBm"/>
+      </property>
+      <property name="pixmap">
+       <pixmap>../icon/network/network-wireless-signal-ok-symbolic.png</pixmap>
+      </property>
+      <property name="scaledContents">
+       <bool>false</bool>
+      </property>
+     </widget>
+     <widget class="QLabel" name="labelBattery">
+      <property name="geometry">
+       <rect>
+        <x>157</x>
+        <y>43</y>
+        <width>31</width>
+        <height>31</height>
+       </rect>
+      </property>
+      <property name="toolTip">
+       <string extracomment="50%"/>
+      </property>
+      <property name="statusTip">
+       <string extracomment="50%"/>
+      </property>
+      <property name="pixmap">
+       <pixmap>../icon/battery/battery-good-charging-symbolic.png</pixmap>
+      </property>
+      <property name="scaledContents">
+       <bool>false</bool>
+      </property>
+     </widget>
+     <widget class="QLabel" name="labelOTA">
+      <property name="geometry">
+       <rect>
+        <x>198</x>
+        <y>45</y>
+        <width>36</width>
+        <height>26</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string/>
+      </property>
+      <property name="pixmap">
+       <pixmap>../icon/ota/updating.png</pixmap>
+      </property>
+     </widget>
+     <zorder>labelDeviceType</zorder>
+     <zorder>labelSignal</zorder>
+     <zorder>labelBattery</zorder>
+     <zorder>labelDeviceID</zorder>
+     <zorder>labelOTA</zorder>
+    </widget>
+   </item>
+   <item>
+    <widget class="QWidget" name="widgetControls" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>75</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>66</height>
+      </size>
+     </property>
+     <widget class="QLabel" name="labelStatus">
+      <property name="geometry">
+       <rect>
+        <x>-26</x>
+        <y>44</y>
+        <width>181</width>
+        <height>31</height>
+       </rect>
+      </property>
+      <property name="font">
+       <font>
+        <pointsize>10</pointsize>
+        <italic>true</italic>
+       </font>
+      </property>
+      <property name="text">
+       <string>Last Update: 2:30AM</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+      </property>
+     </widget>
+     <widget class="QToolButton" name="buttonMenu">
+      <property name="geometry">
+       <rect>
+        <x>131</x>
+        <y>10</y>
+        <width>21</width>
+        <height>36</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Activate </string>
+      </property>
+      <property name="autoRaise">
+       <bool>false</bool>
+      </property>
+      <property name="arrowType">
+       <enum>Qt::DownArrow</enum>
+      </property>
+     </widget>
+     <widget class="QToolButton" name="buttonPair">
+      <property name="geometry">
+       <rect>
+        <x>41</x>
+        <y>10</y>
+        <width>91</width>
+        <height>36</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Pair</string>
+      </property>
+      <property name="checkable">
+       <bool>true</bool>
+      </property>
+      <property name="popupMode">
+       <enum>QToolButton::DelayedPopup</enum>
+      </property>
+      <property name="toolButtonStyle">
+       <enum>Qt::ToolButtonTextOnly</enum>
+      </property>
+     </widget>
+     <zorder>labelStatus</zorder>
+     <zorder>buttonMenu</zorder>
+     <zorder>buttonPair</zorder>
+     <zorder>widgetInfo</zorder>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 38 - 7
qt/mainwindow.ui

@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>525</width>
-    <height>648</height>
+    <height>753</height>
    </rect>
   </property>
   <property name="sizePolicy">
@@ -60,7 +60,7 @@
        <enum>QTabWidget::West</enum>
       </property>
       <property name="currentIndex">
-       <number>0</number>
+       <number>2</number>
       </property>
       <property name="elideMode">
        <enum>Qt::ElideNone</enum>
@@ -136,8 +136,8 @@
             <rect>
              <x>0</x>
              <y>0</y>
-             <width>459</width>
-             <height>556</height>
+             <width>456</width>
+             <height>658</height>
             </rect>
            </property>
           </widget>
@@ -332,7 +332,7 @@
           </property>
          </widget>
         </item>
-        <item alignment="Qt::AlignTop">
+        <item>
          <widget class="QGroupBox" name="groupDeviceSettings">
           <property name="title">
            <string>Device Settings</string>
@@ -369,7 +369,11 @@
             </widget>
            </item>
            <item row="2" column="1">
-            <widget class="QLineEdit" name="wifiSSID"/>
+            <widget class="QLineEdit" name="wifiSSID">
+             <property name="clearButtonEnabled">
+              <bool>true</bool>
+             </property>
+            </widget>
            </item>
            <item row="3" column="0">
             <widget class="QLabel" name="labelPassword">
@@ -379,7 +383,31 @@
             </widget>
            </item>
            <item row="3" column="1">
-            <widget class="QLineEdit" name="wifiPassword"/>
+            <widget class="QLineEdit" name="wifiPassword">
+             <property name="clearButtonEnabled">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item row="5" column="0">
+            <widget class="QLabel" name="labelPort">
+             <property name="text">
+              <string>Port:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="5" column="1">
+            <widget class="QComboBox" name="selectPort"/>
+           </item>
+           <item row="6" column="1">
+            <widget class="QToolButton" name="buttonRefreshPorts">
+             <property name="layoutDirection">
+              <enum>Qt::LeftToRight</enum>
+             </property>
+             <property name="text">
+              <string>Refresh Ports</string>
+             </property>
+            </widget>
            </item>
           </layout>
          </widget>
@@ -389,6 +417,9 @@
           <property name="standardButtons">
            <set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
           </property>
+          <property name="centerButtons">
+           <bool>false</bool>
+          </property>
          </widget>
         </item>
         <item>

+ 4 - 0
wsc-tools-clear-topics.sh

@@ -0,0 +1,4 @@
+#!/bin/sh
+echo "cleaning " $1 " :: usage: cleanmqtt <host>"
+mosquitto_sub -h $1 -t "#" -v | while read line _; do mosquitto_pub -h $1 -t "$line" -r -n; done
+echo "done"

+ 9 - 2
wsc_client.py

@@ -1,10 +1,10 @@
 import sys, time, collections
-from PyQt5.QtCore import QObject, pyqtSignal, QSettings
+from PyQt5.QtCore import QObject, pyqtSignal, QSettings, QTimer
 import paho.mqtt.client as mqtt
 import logging
 import wsc_device
 import wsc_device_encoder
-from wsc_tools import ParsingTools
+from wsc_tools import ParsingTools, EncoderConfig
 
 #FORMAT = '%(asctime)-15s'
 logging.basicConfig( level=logging.DEBUG)
@@ -41,6 +41,7 @@ class WSC_Client(QObject):
 
         self._mqttc.on_connect = self.wsc_on_connect
         self._mqttc.on_disconnect = self.wsc_on_disconnect
+        self.encoderConfig = EncoderConfig()
 
         if self.__mqttDebug:
             self._mqttc.on_log = self.mqtt_on_log
@@ -88,6 +89,7 @@ class WSC_Client(QObject):
             self.port = port
 
         try:
+            self._mqttc.reconnect_delay_set(1,120)
             self._mqttc.connect(self.ip, int(self.port), self.keepAlive)
             self.__encoderManager.setupMQTT()
             if gui: 
@@ -103,6 +105,11 @@ class WSC_Client(QObject):
              self.networkStatus.emit("Oh-no! Broker settings are incorrect or there is a network failure")
              #sys.exit(1)
 
+    def connectionTimeout(self):
+        self.killWSC() 
+        self.networkUpdate.emit("Oh-no! Broker settings are incorrect or there is a network failure")
+        log.info("connection timeout")
+
     def restartWSC(self):
         self.killWSC()
         self.networkUpdate.emit("Restarting WSC...")

+ 82 - 9
wsc_device_encoder.py

@@ -28,6 +28,7 @@ class Encoder():
     UART_TX_COMMAND_TOPIC = "uart"
     COMMAND_RETAIN = False 
     COMMAND_QOS = 1 
+    DATA_BUTTON_QOS = 0
 
     def __init__(self, device_id, mqttc):
         self.__device_id  = device_id
@@ -67,11 +68,18 @@ class Encoder():
                            'shutdown':  self.shutdown}
         self.__mqttc = mqttc
         self.switchOne = Switch()
+        self.switchOne.setConfig("1")
         self.switchTwo = Switch()
+        self.switchTwo.setConfig("2")
         self.switchAdaptive = Switch()
         self.RAW_COMMAND_TOPIC = "encoder/" + device_id + "/command/"
+        self.RAW_DATA_TOPIC = "encoder/" + device_id + "/data/"
+        self.__previous_button_payload = 0b0
 
 
+    def getDeviceID(self):
+        return self.__device_id
+
     def updateField(self, field, value):
         if field in self.__fields.keys():
             self.__fields[field] = value
@@ -95,17 +103,62 @@ class Encoder():
     def getField(self, field):
         return self.__fields[field]
 
+    def activate(self):
+        self.__mqttc.subscribe(self.RAW_DATA_TOPIC + "button", qos=self.DATA_BUTTON_QOS)
+        self.__mqttc.message_callback_add(self.RAW_DATA_TOPIC + "button", self.data_button_cb)
+        log.info("activated device" + self.__device_id)
+
+    def deactivate(self):
+        self.__mqttc.unsubscribe(self.RAW_DATA_TOPIC + "button")
+        log.info("deactivated device" + self.__device_id)
+
+
+    # This needs to be modified for the following: 
+    # 1. only activate switch if there is change....i.e. don't activating switch 1 again which switch 1 is already
+    #    depressed and switch 2 is pressed 
+    # 2. don't be pushing mad buttons because shit is broken 
+
+    def data_button_cb(self, mqttc, backend_data, msg):
+        topic = msg.topic
+        payload = int.from_bytes(msg.payload, 'little') 
+        switchTwo = 0b100
+        switchOne = 0b100000
+    
+
+        # press / release key
+        if ((payload & switchOne) ^ (self.__previous_button_payload & switchOne)): 
+            print("switchOne Change")
+            if (payload & switchOne): 
+                self.switchOne.releaseKey() 
+            else: 
+                print("Activated!")
+                self.switchOne.pressKey() 
+
+        if ((payload & switchTwo) ^ (self.__previous_button_payload & switchTwo)):
+            print("switchTwo Change")
+            if (payload & switchTwo):
+                self.switchTwo.releaseKey() 
+            else: 
+                print("Activated!")
+                self.switchTwo.pressKey()
+
+        self.__previous_button_payload = payload 
+        log.debug("encoder " + self.__device_id + " button payload: " + bin(payload))
+
     def update(self): 
-        self.__mqttc.publish(self.RAW_COMMAND_TOPIC+self.OTA_COMMAND_TOPIC, b'1', qos=1, retain=False)
+        self.__mqttc.publish(self.RAW_COMMAND_TOPIC+self.OTA_COMMAND_TOPIC, b'1', qos=self.COMMAND_QOS, retain=False)
         log.info("sent OTA command")
 
     def restart(self):
-        self.__mqttc.publish(self.RAW_COMMAND_TOPIC+self.RESTART_COMMAND_TOPIC, b'1', qos=1, retain=False)
+        self.__mqttc.publish(self.RAW_COMMAND_TOPIC+self.RESTART_COMMAND_TOPIC, b'1', qos=self.COMMAND_QOS, retain=False)
         log.info("sent restart command")  
 
     def shutdown(self): 
-        self.__mqttc.publish(self.RAW_COMMAND_TOPIC+self.SHUTDOWN_COMMAND_TOPIC, b'1', qos=1, retain=False)
+        self.__mqttc.publish(self.RAW_COMMAND_TOPIC+self.SHUTDOWN_COMMAND_TOPIC, b'1', qos=self.COMMAND_QOS, retain=False)
         log.info("sent shutdown command")  
+    
+    def locate(self):
+        self.__mqttc.publish(self.RAW_COMMAND_TOPIC+self.LOCATE_COMMAND_TOPIC, b'1', qos=self.COMMAND_QOS, retain=False)
 
 class EncoderUI(QtWidgets.QWidget):
     sendCommand = QtCore.pyqtSignal(['QString'], name='sendCommand')
@@ -144,6 +197,10 @@ class EncoderUI(QtWidgets.QWidget):
     def __init__(self, encoder):
         self.__deviceName = None
         self.__encoder = encoder
+        self.__org = 'IdeasX'
+        self.__app = 'Workstation-Client'
+
+        self.restoreSettings()
 
         # Setup UI components
         super(EncoderUI, self).__init__()
@@ -154,16 +211,32 @@ class EncoderUI(QtWidgets.QWidget):
 
         # Setup Signals
         self.setupMenu()
-        #self.__ui.buttonActivate.clicked.connect(self.activateEncoder)
+        self.__ui.buttonActivate.clicked.connect(self.activateEncoder)
         self.__ui.buttonSwitchOne.clicked.connect(lambda: self.openSwitchDialog(self.__encoder.switchOne))
         self.__ui.buttonSwitchTwo.clicked.connect(lambda: self.openSwitchDialog(self.__encoder.switchTwo))
         #self.activateDevice.connect(self.__wsc.activateEncoder)
         #self.deactivateDevice.connect(self.__wsc.deactivateEncoder)
 
+    def saveSettings(self, device_name):
+        settings = QtCore.QSettings(self.__org, self.__app)
+        settings.beginGroup(self.__encoder.getDeviceID())
+        settings.setValue("name", device_name)
+        settings.setValue("type", "encoder")
+        settings.endGroup()
+        self.setDeviceAlisas(device_name)
+
+    def restoreSettings(self):
+        settings = QtCore.QSettings(self.__org, self.__app)
+        settings = QtCore.QSettings(self.__org, self.__app)
+        settings.beginGroup(self.__encoder.getDeviceID())
+        self.__deviceName = settings.value("name", self.__encoder.getDeviceID())
+        settings.endGroup()
+
+
     def openDeviceInformation(self):
         dialog = InfoUI()
         dialog.updateDisplay(self.__encoder.listFields())
-        dialog.newDeviceName.connect(self.setDeviceAlisas)
+        dialog.newDeviceName.connect(self.saveSettings)
         dialog.exec()
 
     def openSwitchDialog(self, switch):
@@ -211,12 +284,10 @@ class EncoderUI(QtWidgets.QWidget):
 
     def activateEncoder(self):
         if self.__ui.buttonActivate.text() == "Activate":
-            log.info("Activating Encoder: " + self.__ui.labelModuleID.text())
-            self.activateDevice.emit(self.__strModuleID, self.__deviceType)
+            self.__encoder.activate()
             self.__ui.buttonActivate.setText("Deactivate")
         else:
-            log.info("Deactivating Encoder: " + self.__ui.labelModuleID.text())
-            self.deactivateDevice.emit(self.__strModuleID, self.__deviceType)
+            self.__encoder.deactivate()
             self.__ui.buttonActivate.setText("Activate")
 
     def updateDevice(self, encoder):
@@ -230,6 +301,8 @@ class EncoderUI(QtWidgets.QWidget):
 
         if self.__deviceName == None:
             self.setModuleID(self.__strModuleID)
+        else: 
+            self.setDeviceAlisas(self.__deviceName)
         self.setSOCIcon(self.__soc)
         self.setRSSIIcon(self.__rssi)
         self.setStatusTime(self.__updateTime)

+ 115 - 10
wsc_tools.py

@@ -1,4 +1,103 @@
 import  pyautogui as ue
+import serial 
+import serial.tools.list_ports
+import logging
+import time
+
+class EncoderConfig():
+    BAUD_RATE = 115200
+    SET_AP_COMMAND = "AT+WA="
+    CLEAR_APS_COMMAND = "AT+WC=" 
+    LIST_APS_COMMAND = "AT+WL"
+    
+    def __init__(self):
+        self.__ports = None
+        self.__ser = None
+        self.__port = None
+
+    def setPort(self, port):        
+        if self.__ser != None:
+            self.__ser.close() 
+        self.__port = port
+
+    def close(self):
+        if self.__ser != None:
+          self.__ser.close()
+
+    def connect(self, port=None):
+        if port != None: 
+            self.port = port 
+        self.__ser = serial.Serial(port = self.port, baudrate=EncoderConfig.BAUD_RATE, timeout=3)
+        #self.__ser.open()
+
+    def reset(self): 
+        self.__ser.setDTR(value=1) 
+        time.sleep(0.5)
+        self.__ser.setDTR(value=0)
+        time.sleep(0.5)
+
+    def getPort(self): 
+        return self.__port
+
+    def getPorts(self):
+        temp = []
+        self.__ports = serial.tools.list_ports.comports()
+        for port in self.__ports: 
+            temp.append(port[0])
+        return temp    
+
+    def clearWifiAPs(self, port): 
+        self.connect(port)
+        clear_str = "\n\r"
+        clear_str = clear_str.encode('utf-8')
+        self.__ser.write(clear_str)
+        
+    
+    def setWifiAPs(self, aps, port): 
+        self.connect(port)
+        clear_str = "\n\r"
+        clear_str = clear_str.encode('utf-8')
+        time.sleep(0.5)
+        self.__ser.write(clear_str)
+        time.sleep(0.5)
+        factory_reset_str = "\n\rAT+FRST\n\r"
+        factory_reset_str = factory_reset_str.encode('utf-8')
+        self.__ser.write(factory_reset_str)
+        time.sleep(0.5)
+        for creds in aps: 
+            self.setWifiAP(creds[0], creds[1])
+            time.sleep(1)
+
+        self.__ser.write(clear_str)
+        time.sleep(0.5)
+        ## do hard reset 
+        self.reset()
+        
+        ##reset_str = "\n\rAT+RST\r\n"
+        ##reset_str = reset_str.encode('utf-8')
+        ##self.__ser.write(reset_str)
+        
+        self.__ser.close()
+
+    def setWifiAP(self, ssid, password=None):
+        if not self.__ser.isOpen():
+            return 
+    
+        command_str = EncoderConfig.SET_AP_COMMAND + ssid + "," + password + "\r\n"
+        command_str = command_str.encode('utf-8')
+        self.__ser.write(command_str)        
+
+    def clearWifiAPs(self, port):
+        pass
+
+    def getWifiConfig(self): 
+        pass
+
+    def setBrokerConfig(self):
+        pass
+
+    def getBrokerConifg(self): 
+        pass
 
 class ParsingTools():
     def macToString(self, mac_bytes):
@@ -15,7 +114,11 @@ class ParsingTools():
 
     def calculateSOC(self, raw_SOC):
         raw_SOC = int(raw_SOC.decode('utf-8'))
-        return raw_SOC.to_bytes(2, 'big')[0]
+        soc = raw_SOC.to_bytes(2, 'big')[0]
+        if (soc > 100):
+            return 100
+        else: 
+            return raw_SOC.to_bytes(2, 'big')[0]
 
     def getIDfromTopic(self, topic):
         return topic.split('/')[1]
@@ -73,7 +176,7 @@ class Switch():
 
     def setConfig(self, key, latch=False, interval=0.0, release=False, enable=True):
         # release old key if currently held down
-        self.releaseKey() 
+        self.releaseKey(force=True) 
         self.__latchState = 0
         self.active = enable
 
@@ -88,15 +191,17 @@ class Switch():
     def deactivate(self): 
         self.active = False
     
-    def pressKey(self): 
-        if self.__release: 
-            ue.keyDown(self.__key)
-        else: 
-            ue.typewrite(self.__key, interval=self.__interval)
+    def pressKey(self, force=False): 
+        if self.active or force: 
+            if self.__release: 
+                ue.keyDown(self.__key)
+            else: 
+                ue.typewrite(self.__key, interval=self.__interval)
     
-    def releaseKey(self): 
-        if self.__release:
-            ue.keyUp(self.__key)
+    def releaseKey(self, force=False): 
+        if self.active or force: 
+            if self.__release:
+                ue.keyUp(self.__key)
         
 
 

+ 77 - 5
wsc_ui.py

@@ -2,12 +2,12 @@ import logging
 import sys
 import time
 import sip
-from wsc_tools import ParsingTools
+from wsc_tools import ParsingTools, EncoderConfig
 from wsc_client import WSC_Client
 from wsc_device_encoder import EncoderUI
 
 from PyQt5 import QtCore, QtGui, QtWidgets
-from pyqt.mainwindow2 import Ui_MainWindow
+from pyqt.mainwindow import Ui_MainWindow
 from pyqt.ideasxdevice import Ui_IdeasXDevice
 from pyqt.encoderconfigurationdialog import Ui_SwitchConfigDialog
 from pyqt.devicedialog import Ui_Dialog
@@ -26,6 +26,11 @@ class UIDeviceManager():
         self.__deviceLayout.setSpacing(0)
         self.__wsc = wsc
 
+    def restart(self):
+        deviceClass = self.__deviceClass
+        wsc = self.__wsc 
+        self.__init(deviceClass, wsc)
+
     def refreshDevices(self, devices):
         for deviceMAC in list(devices.keys()):
             if deviceMAC in self.__devices.keys():
@@ -51,7 +56,6 @@ class UIDeviceManager():
     def printDevices(self):
         print(self.__devices)
 
-
 class MainWindow(QtWidgets.QMainWindow):
     def __init__(self, wsc):
         super(MainWindow, self).__init__()
@@ -67,16 +71,69 @@ class MainWindow(QtWidgets.QMainWindow):
         self.__ui.statusMessageWidget.setText("Starting WSC...")
         self.__ui.statusMessageWidget.setAlignment(QtCore.Qt.AlignLeft)
         self.__ui.statusbar.addWidget(self.__ui.statusMessageWidget, 1)
+        self.__ui.tabWidget.setCurrentIndex(0)                              # set to main window 
 
         self.__org = 'IdeasX'
         self.__app = 'Workstation-Client'
 
         self.restoreSettings()
+        self.refreshSerialPorts()
+        self.selectSerialPort()
 
+        #setup UI signals 
         #self.__ui.buttonSettings.clicked.connect(self.updateBrokerSettings)
         self.__ui.buttonBoxNetwork.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.updateBrokerSettings)
         self.__ui.buttonBoxNetwork.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.restoreBrokerSettings)
-
+        self.__ui.buttonRefreshPorts.clicked.connect(self.refreshSerialPorts)
+        #self.__ui.selectPort.activated.connect(self.selectSerialPort)
+        self.__ui.selectAP.valueChanged.connect(self.selectDisplayedAP)
+        self.__ui.wifiSSID.textEdited.connect(self.updateAP)
+        self.__ui.wifiPassword.textEdited.connect(self.updateAP)
+        self.__ui.buttonBoxDevice.button(QtWidgets.QDialogButtonBox.Save).clicked.connect(self.saveEncoderConfiguration)
+       # self.__ui.selectPort.activated.connect(self.updatePort)
+        #self.__ui.buttonBoxDevice.button(QtWidgets.QDialog.Save).clicked.connect(self.__wsc.encoderConfig.)
+
+    def updateAP(self):
+        index = self.__ui.selectAP.value() - 1
+        self.__AccessPoints[index][0] = self.__ui.wifiSSID.text()
+        self.__AccessPoints[index][1] = self.__ui.wifiPassword.text()
+
+    def selectDisplayedAP(self): 
+        print(self.__ui.selectAP.value()-1)
+
+        index = self.__ui.selectAP.value() - 1
+        ssid = self.__AccessPoints[index][0]
+        password = self.__AccessPoints[index][1]
+        self.__ui.wifiSSID.setText(ssid)
+        self.__ui.wifiPassword.setText(password)
+
+    def saveEncoderConfiguration(self): 
+        log.info("storing wi-fi credientials as defaults")
+        settings = QtCore.QSettings(self.__org, self.__app)
+        settings.beginGroup("Accesspoints")
+        settings.setValue("Credentials", self.__AccessPoints)
+        settings.endGroup()
+        try: 
+            log.info("training device")
+            self.setStatusBarUpdate("Training Device...")
+            self.__wsc.encoderConfig.setWifiAPs(self.__AccessPoints, self.__ui.selectPort.currentText())
+            self.setStatusBarUpdate("Device Successfully Trained")
+        except Exception as e: 
+            self.__wsc.encoderConfig.close()
+            log.warning("failure training device")
+            log.warning(str(e))
+            self.setStatusBarUpdate("Device Training Failed: " + str(e))
+
+    def selectSerialPort(self): 
+        port = self.__ui.selectPort.currentText()
+        self.__wsc.encoderConfig.setPort(port)
+        log.info("updated serial port to " + port)
+
+    def refreshSerialPorts(self): 
+        ports = self.__wsc.encoderConfig.getPorts()
+        self.__ui.selectPort.clear()
+        self.__ui.selectPort.addItems(ports)
+        self.__ui.selectPort.setEditable(True) # add to UI file
 
     def setEncoderLayout(self, layout):
         self.__ui.contentEncoder.setLayout(layout)
@@ -136,6 +193,21 @@ class MainWindow(QtWidgets.QMainWindow):
         self.__OTAServer = settings.value('OTAServer', 'ideasx.duckdns.org')
         settings.endGroup()
 
+        self.__ui.otaServer.setText(self.__OTAServer)
+
+        settings.beginGroup("Accesspoints")
+        default_APs = [ ['curiousmuch-ThinkPad-T450s', '9dHM8nVi'],
+                        ['',''],
+                        ['',''],
+                        ['',''],
+                        ['',''] ]
+        self.__AccessPoints = settings.value("Credentials", default_APs)
+        #self.__AccessPoints = default_APs
+        settings.endGroup()
+
+        self.__ui.wifiSSID.setText(self.__AccessPoints[0][0])
+        self.__ui.wifiPassword.setText(self.__AccessPoints[0][1])
+
     def restoreBrokerSettings(self):
         settings = QtCore.QSettings(self.__org, self.__app)
 
@@ -170,7 +242,7 @@ if __name__ == "__main__":
     wsc.networkStatus.connect(mainWindow.setStatusBarMessage)
     wsc.networkUpdate.connect(mainWindow.setStatusBarUpdate)
 
+    mainWindow.show()
     wsc.StartWorkstationClient(gui=True)
 
-    mainWindow.show()
     sys.exit(app.exec_())