Browse Source

GUI Updates

curiousmuch 7 years ago
parent
commit
0ede436f32

+ 1 - 0
IdeasXDatabaseManager.py

@@ -140,6 +140,7 @@ class IdeasXDatabaseManager():
     def clearDatabase(self):
         self.printError("Failed to delete database")
         
+        
 if __name__ == '__main__':
     dbm = IdeasXDatabaseManager()
     msg = IdeasXMessages.HealthMessage()

+ 76 - 34
gui_example.py → IdeasXWSCGUI.py

@@ -1,7 +1,11 @@
 import sys
+import time
 from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
 from mainwindow import Ui_MainWindow
 from ideasxdevice import Ui_IdeasXDevice
+from WorkstationClientClass import WorkstationClientClass
+from ParsingToolsClass import ParsingTools
+
 
 def generateMACID():
     import numpy as np
@@ -32,16 +36,42 @@ def generateStatus():
     return statusStr
 
 class IdeasXEncoder(QtWidgets.QWidget):
-    def __init__(self):
+    def __init__(self, wsc, encoder): #, encoderID, encoderRSSI, encoderBattery, encoderStatus):
         super(IdeasXEncoder, self).__init__()
         self.ui = Ui_IdeasXDevice()
         self.ui.setupUi(self)
-        self.ui.labelModuleID.setText(generateMACID())
-        self.ui.labelRSSI.setText(generateRSSI())
-        self.ui.labelBattery.setText(generateSOC())
-        self.ui.labelStatus.setText(generateStatus())
+#         self.ui.labelModuleID.setText(encoderID)
+#         self.ui.labelRSSI.setText(encoderRSSI)
+#         self.ui.labelBattery.setText(encoderBattery)
+#         self.ui.labelStatus.setText("I don't really have something here yet...")
+        self.parserTools = ParsingTools()
+        self.wsc = wsc
+
+        self.ui.labelModuleID.setText(self.parserTools.macToString(encoder['module_id']))
+        self.ui.labelRSSI.setText("RSSI: " + str(encoder['rssi']) + "dBm")
+        self.ui.labelBattery.setText("Battery: " + str(self.parserTools.calculateSOC(encoder['soc'])) + "%" +
+                                     " (" + str(round(self.parserTools.calculateVCell(encoder['vcell']), 3)) +"V)")
+        self.ui.labelStatus.setText("Last Update: " + time.ctime(encoder['time']))
         self.ui.buttonActivate.clicked.connect(self.activateEncoder)
         
+        deviceMenu = QtWidgets.QMenu()
+        deviceMenu.addSection("General Actions")
+        deviceMenu.addAction("Pair Encoder with Actuator")
+        deviceMenu.addAction("Train Adaptive Switch")
+        deviceMenu.addAction("Configure Module")
+        deviceMenu.addSection("Encoder Commands")
+        deviceMenu.addAction("Shutdown Encoder")
+        deviceMenu.addAction("Restart Encoder")
+        deviceMenu.addAction("Update Firmware")
+        
+        #deviceMenu.setLayoutDirection(QtCore.Qt.LeftToRight)
+        self.ui.buttonMenu.setPopupMode(2)
+        self.ui.buttonMenu.setMenu(deviceMenu)
+        self.ui.buttonMenu.setStyleSheet("* { padding-right: 3px } QToolButton::menu-indicator { image: none }")
+#         self.ui.buttonMenu.setToolbarButtonSytel
+        
+        
+             
     def activateEncoder(self):
         if self.ui.buttonActivate.text() == "Activate":
             print("Activating Encoder: " + self.ui.labelModuleID.text())
@@ -68,6 +98,13 @@ class IdeasXMainWindow(QtWidgets.QMainWindow):
         super(IdeasXMainWindow, self).__init__()
         self.ui = Ui_MainWindow()
         self.ui.setupUi(self)
+        'module_id', b'\x18\xfe4\xf1\xf2\x8d'
+        self.encoders = {'23:4d:12:2a:ad': {'module_id': b'\x18\xfe4\xf1\xf2\x8d', 'rssi': -54, 'soc': 32432}, 
+                         '43:3d:12:3a:ad': {'module_id': b'\x18\xfe4\xf1\xf2\x8d', 'rssi': -73, 'soc': 24243}}
+        
+#         self.wsc = WorkstationClientClass()
+#         self.wsc.guiStartWorkstationClient('10.41.0.1')
+#         self.wsc.refreshCallBack(self.refreshList)
         
         #self.ui.searchEncoder.textChanged.connect(self.filterEncoders)
 #         itemN = QtWidgets.QListWidgetItem()
@@ -79,30 +116,34 @@ class IdeasXMainWindow(QtWidgets.QMainWindow):
         #widgetLayout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)
         #widget.setLayout(widgetLayout)
         
-        self.widgetIndex = []
-        self.widgetNames = []
-        self.itemIndex = []
-        for i in range(100):
-            self.itemIndex.append(QtWidgets.QListWidgetItem())
-            self.widgetIndex.append(IdeasXEncoder())
-            itemN = self.itemIndex[i]
-            widget = self.widgetIndex[i]
-            itemN.setSizeHint(widget.minimumSize())
-         
-            #widget = IdeasXEncoder()
-            self.ui.listEncoder.addItem(itemN)
-            self.ui.listEncoder.setItemWidget(itemN, widget)
-            self.widgetNames.append(widget.ui.labelModuleID.text())
-            
-        print("List of Encoder IDs: ")
-        print(self.widgetNames)
+#         self.widgetIndex = []
+#         self.widgetNames = []
+#         self.itemIndex = []
+#         for i in range(100):
+#             self.itemIndex.append(QtWidgets.QListWidgetItem())
+#             self.widgetIndex.append(IdeasXEncoder())
+#             itemN = self.itemIndex[i]
+#             widget = self.widgetIndex[i]
+#             itemN.setSizeHint(widget.minimumSize())
+#          
+#             #widget = IdeasXEncoder()
+#             self.ui.listEncoder.addItem(itemN)
+#             self.ui.listEncoder.setItemWidget(itemN, widget)
+#             self.widgetNames.append(widget.ui.labelModuleID.text())
+#             
+#         print("List of Encoder IDs: ")
+#         print(self.widgetNames)
+        self.wsc = WorkstationClientClass()
+        #self.wsc.attachRefreshCallback(self.generateEncoderList)
+        self.wsc.guiStartWorkstationClient('ideasx.duckdns.org')
+        #self.generateEncoderList()
         
     def generateEncoders(self):
         self.widgetIndex = []
         self.itemIndex = []
         for i in range(100):
             itemN = QtWidgets.QListWidgetItem()
-            self.widgetIndex.append(IdeasXEncoder())
+            self.widgetIndex.append(IdeasXEncoder(self.wsc))
             widget = self.widgetIndex[i]
             itemN.setSizeHint(widget.minimumSize())
          
@@ -110,24 +151,23 @@ class IdeasXMainWindow(QtWidgets.QMainWindow):
             self.ui.listEncoder.addItem(itemN)
             self.ui.listEncoder.setItemWidget(itemN, widget)
 #             
-    def generateEncoder(self):
+    def generateEncoder(self, wsc, encoder):
         itemN = QtWidgets.QListWidgetItem()
         #self.widgetIndex.append(IdeasXEncoder())
-        widget = IdeasXEncoder()#self.widgetIndex[-1]
+        widget = IdeasXEncoder(wsc, encoder)#self.widgetIndex[-1]
         itemN.setSizeHint(widget.minimumSize())
         self.ui.listEncoder.addItem(itemN)
         self.ui.listEncoder.setItemWidget(itemN, widget)
+        return widget
         
     def clearEncoders(self):
         self.ui.listEncoder.clear()
         self.widgetIndex = []
         
-    def refreshList(self):
-        #self.clearEncoders()
-        for widget in self.widgetIndex: 
-            widget.ui.labelBattery.setText(generateSOC())
-            widget.ui.labelStatus.setText(generateStatus())
-            widget.ui.labelRSSI.setText(generateRSSI())
+    def generateEncoderList(self):
+        self.clearEncoders()
+        for encoderMAC in self.wsc.encoders.keys():
+            self.generateEncoder(self.wsc, self.wsc.encoders[encoderMAC])
             
     def filterEncoders(self):
         searchInput = self.ui.searchEncoder.text()
@@ -147,13 +187,15 @@ class IdeasXMainWindow(QtWidgets.QMainWindow):
         
 if __name__ == "__main__":
     app = QtWidgets.QApplication(sys.argv)
-    app.setWindowIcon(QtGui.QIcon('icon/ideasx2.png'))
+    app.setWindowIcon(QtGui.QIcon('icon/ideasx.png'))
     mainWindow = IdeasXMainWindow()
     mainWindow.show()
     
     timer = QtCore.QTimer()
-    timer.timeout.connect(mainWindow.refreshList)
+    timer.timeout.connect(mainWindow.generateEncoderList)
     timer.start(1000)
-    
+    time.sleep(2)
+    mainWindow.generateEncoderList()
+     
     sys.exit(app.exec_())
     

+ 24 - 0
ParsingToolsClass.py

@@ -0,0 +1,24 @@
+class ParsingTools():
+    def macToString(self, mac_bytes):
+        ''' Convert uint8 byte string to "XX:XX:XX:XX:XX" 
+        '''
+        mac_str = ""
+        for byte in mac_bytes: 
+            mac_str = mac_str + format(byte, 'x') + ':'
+        return mac_str[:-1].format('utf-8')
+            
+    def calculateVCell(self, raw_Vcell):
+        return raw_Vcell*1.25e-3
+    
+    def calculateSOC(self, raw_SOC):
+        return raw_SOC.to_bytes(2, 'big')[0]
+    
+
+if __name__ == '__main__':
+    print("I never developed self-test, but if I did they would go here.")
+    
+    pt = ParsingTools()
+    
+    print("Testing macToString")
+    print(pt.macToString(b'023430'))
+    

+ 195 - 128
Qt/ideasxdevice.ui

@@ -6,151 +6,218 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>416</width>
-    <height>70</height>
+    <width>506</width>
+    <height>84</height>
    </rect>
   </property>
   <property name="sizePolicy">
-   <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <property name="minimumSize">
    <size>
-    <width>416</width>
-    <height>70</height>
+    <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>
-  <widget class="QPushButton" name="buttonActivate">
-   <property name="geometry">
-    <rect>
-     <x>289</x>
-     <y>11</y>
-     <width>121</width>
-     <height>31</height>
-    </rect>
-   </property>
-   <property name="autoFillBackground">
-    <bool>false</bool>
-   </property>
-   <property name="text">
-    <string>Activate</string>
-   </property>
-   <property name="checkable">
-    <bool>true</bool>
-   </property>
-   <property name="checked">
-    <bool>false</bool>
-   </property>
-  </widget>
-  <widget class="QLabel" name="labelModuleID">
-   <property name="geometry">
-    <rect>
-     <x>110</x>
-     <y>10</y>
-     <width>231</width>
-     <height>31</height>
-    </rect>
-   </property>
-   <property name="font">
-    <font>
-     <family>Roboto Condensed</family>
-     <pointsize>20</pointsize>
-     <italic>false</italic>
-    </font>
-   </property>
-   <property name="frameShape">
-    <enum>QFrame::NoFrame</enum>
-   </property>
-   <property name="text">
-    <string>2f:32:56:4f:72</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="labelStatus">
-   <property name="geometry">
-    <rect>
-     <x>280</x>
-     <y>50</y>
-     <width>127</width>
-     <height>16</height>
-    </rect>
-   </property>
-   <property name="font">
-    <font>
-     <family>DejaVu Sans</family>
-     <pointsize>6</pointsize>
-     <italic>true</italic>
-    </font>
-   </property>
-   <property name="text">
-    <string>Last Update: 7:46AM</string>
-   </property>
-   <property name="alignment">
-    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-   </property>
-  </widget>
-  <widget class="QLabel" name="labelBattery">
-   <property name="geometry">
-    <rect>
-     <x>110</x>
-     <y>40</y>
-     <width>91</width>
-     <height>16</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>Battery:  98%</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="labelRSSI">
-   <property name="geometry">
-    <rect>
-     <x>110</x>
-     <y>50</y>
-     <width>91</width>
-     <height>16</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>RSSI:  -54dBm</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="label">
-   <property name="geometry">
-    <rect>
-     <x>10</x>
-     <y>0</y>
-     <width>91</width>
-     <height>71</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string/>
-   </property>
-   <property name="pixmap">
-    <pixmap>../../../Dropbox/curiousmuch/20160820/Module V0.3 Artwork 20160820/g4139.png</pixmap>
-   </property>
-  </widget>
-  <widget class="Line" name="line">
-   <property name="geometry">
-    <rect>
-     <x>-13</x>
-     <y>60</y>
-     <width>431</width>
-     <height>20</height>
-    </rect>
-   </property>
-   <property name="orientation">
-    <enum>Qt::Horizontal</enum>
-   </property>
-  </widget>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="QWidget" name="widgetInfo" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>290</width>
+       <height>66</height>
+      </size>
+     </property>
+     <widget class="QLabel" name="label">
+      <property name="geometry">
+       <rect>
+        <x>4</x>
+        <y>5</y>
+        <width>90</width>
+        <height>60</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string/>
+      </property>
+      <property name="pixmap">
+       <pixmap>../../../Dropbox/curiousmuch/20160820/Module V0.3 Artwork 20160820/g4139.png</pixmap>
+      </property>
+     </widget>
+     <widget class="QLabel" name="labelRSSI">
+      <property name="geometry">
+       <rect>
+        <x>100</x>
+        <y>50</y>
+        <width>86</width>
+        <height>16</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>RSSI:  -54dBm</string>
+      </property>
+     </widget>
+     <widget class="QLabel" name="labelModuleID">
+      <property name="geometry">
+       <rect>
+        <x>100</x>
+        <y>0</y>
+        <width>231</width>
+        <height>35</height>
+       </rect>
+      </property>
+      <property name="font">
+       <font>
+        <family>Roboto Condensed</family>
+        <pointsize>20</pointsize>
+        <italic>false</italic>
+       </font>
+      </property>
+      <property name="frameShape">
+       <enum>QFrame::NoFrame</enum>
+      </property>
+      <property name="text">
+       <string>2f:32:56:4f:72:d8</string>
+      </property>
+     </widget>
+     <widget class="QLabel" name="labelBattery">
+      <property name="geometry">
+       <rect>
+        <x>100</x>
+        <y>35</y>
+        <width>166</width>
+        <height>16</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Battery:  98%</string>
+      </property>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <spacer name="horizontalSpacer">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::MinimumExpanding</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>10</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
+   </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>120</width>
+       <height>66</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>-5</x>
+        <y>55</y>
+        <width>116</width>
+        <height>10</height>
+       </rect>
+      </property>
+      <property name="font">
+       <font>
+        <pointsize>6</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>95</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>5</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="autoRaise">
+       <bool>false</bool>
+      </property>
+     </widget>
+    </widget>
+   </item>
+  </layout>
  </widget>
  <resources/>
  <connections/>

+ 51 - 3
Qt/mainwindow.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>480</width>
-    <height>556</height>
+    <width>525</width>
+    <height>648</height>
    </rect>
   </property>
   <property name="sizePolicy">
@@ -39,6 +39,12 @@
     </sizepolicy>
    </property>
    <layout class="QVBoxLayout" name="verticalLayout">
+    <property name="spacing">
+     <number>6</number>
+    </property>
+    <property name="sizeConstraint">
+     <enum>QLayout::SetDefaultConstraint</enum>
+    </property>
     <item>
      <widget class="QTabWidget" name="tabWidget">
       <property name="contextMenuPolicy">
@@ -76,10 +82,25 @@
         <string>Encoders</string>
        </attribute>
        <layout class="QGridLayout" name="gridLayout">
+        <property name="sizeConstraint">
+         <enum>QLayout::SetDefaultConstraint</enum>
+        </property>
+        <property name="leftMargin">
+         <number>9</number>
+        </property>
+        <property name="topMargin">
+         <number>9</number>
+        </property>
+        <property name="rightMargin">
+         <number>9</number>
+        </property>
+        <property name="bottomMargin">
+         <number>9</number>
+        </property>
         <item row="1" column="1">
          <widget class="QLineEdit" name="searchEncoder">
           <property name="placeholderText">
-           <string>Search for Encoder</string>
+           <string>Search for Encoders...</string>
           </property>
           <property name="clearButtonEnabled">
            <bool>true</bool>
@@ -107,6 +128,9 @@
             <verstretch>0</verstretch>
            </sizepolicy>
           </property>
+          <property name="alternatingRowColors">
+           <bool>true</bool>
+          </property>
           <property name="sortingEnabled">
            <bool>false</bool>
           </property>
@@ -119,6 +143,18 @@
         <string>Actuators</string>
        </attribute>
        <layout class="QGridLayout" name="gridLayout_2">
+        <property name="leftMargin">
+         <number>9</number>
+        </property>
+        <property name="topMargin">
+         <number>9</number>
+        </property>
+        <property name="rightMargin">
+         <number>9</number>
+        </property>
+        <property name="bottomMargin">
+         <number>9</number>
+        </property>
         <item row="1" column="1">
          <widget class="QLineEdit" name="searchActuator">
           <property name="autoFillBackground">
@@ -180,6 +216,18 @@
         <string>Settings</string>
        </attribute>
        <layout class="QGridLayout" name="gridLayout_4">
+        <property name="leftMargin">
+         <number>9</number>
+        </property>
+        <property name="topMargin">
+         <number>9</number>
+        </property>
+        <property name="rightMargin">
+         <number>9</number>
+        </property>
+        <property name="bottomMargin">
+         <number>9</number>
+        </property>
         <item row="9" column="0">
          <widget class="QLabel" name="labelPassword">
           <property name="text">

+ 379 - 0
Qt/mainwindow.ui.autosave

@@ -0,0 +1,379 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>525</width>
+    <height>648</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>IdeasX Workstation Client</string>
+  </property>
+  <property name="documentMode">
+   <bool>false</bool>
+  </property>
+  <property name="dockNestingEnabled">
+   <bool>true</bool>
+  </property>
+  <property name="dockOptions">
+   <set>QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::AnimatedDocks|QMainWindow::VerticalTabs</set>
+  </property>
+  <property name="unifiedTitleAndToolBarOnMac">
+   <bool>false</bool>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <property name="sizePolicy">
+    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+     <horstretch>0</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <layout class="QVBoxLayout" name="verticalLayout">
+    <property name="spacing">
+     <number>6</number>
+    </property>
+    <property name="sizeConstraint">
+     <enum>QLayout::SetDefaultConstraint</enum>
+    </property>
+    <item>
+     <widget class="QTabWidget" name="tabWidget">
+      <property name="contextMenuPolicy">
+       <enum>Qt::DefaultContextMenu</enum>
+      </property>
+      <property name="layoutDirection">
+       <enum>Qt::LeftToRight</enum>
+      </property>
+      <property name="autoFillBackground">
+       <bool>true</bool>
+      </property>
+      <property name="tabPosition">
+       <enum>QTabWidget::West</enum>
+      </property>
+      <property name="currentIndex">
+       <number>0</number>
+      </property>
+      <property name="elideMode">
+       <enum>Qt::ElideNone</enum>
+      </property>
+      <property name="documentMode">
+       <bool>false</bool>
+      </property>
+      <property name="tabBarAutoHide">
+       <bool>true</bool>
+      </property>
+      <widget class="QWidget" name="tabEncoder">
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <attribute name="title">
+        <string>Encoders</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout">
+        <property name="sizeConstraint">
+         <enum>QLayout::SetDefaultConstraint</enum>
+        </property>
+        <property name="leftMargin">
+         <number>9</number>
+        </property>
+        <property name="topMargin">
+         <number>9</number>
+        </property>
+        <property name="rightMargin">
+         <number>9</number>
+        </property>
+        <property name="bottomMargin">
+         <number>9</number>
+        </property>
+        <item row="1" column="1">
+         <widget class="QLineEdit" name="searchEncoder">
+          <property name="placeholderText">
+           <string>Search for Encoders...</string>
+          </property>
+          <property name="clearButtonEnabled">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="0">
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="0" column="0" colspan="2">
+         <widget class="QListWidget" name="listEncoder">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="alternatingRowColors">
+           <bool>true</bool>
+          </property>
+          <property name="sortingEnabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="tabActuator">
+       <attribute name="title">
+        <string>Actuators</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_2">
+        <property name="leftMargin">
+         <number>9</number>
+        </property>
+        <property name="topMargin">
+         <number>9</number>
+        </property>
+        <property name="rightMargin">
+         <number>9</number>
+        </property>
+        <property name="bottomMargin">
+         <number>9</number>
+        </property>
+        <item row="1" column="1">
+         <widget class="QLineEdit" name="searchActuator">
+          <property name="autoFillBackground">
+           <bool>false</bool>
+          </property>
+          <property name="frame">
+           <bool>true</bool>
+          </property>
+          <property name="placeholderText">
+           <string>Search for Actuator</string>
+          </property>
+          <property name="clearButtonEnabled">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="0">
+         <spacer name="horizontalSpacer_2">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="0" column="0" colspan="2">
+         <widget class="QTableView" name="tableActuator">
+          <property name="enabled">
+           <bool>true</bool>
+          </property>
+          <property name="frameShadow">
+           <enum>QFrame::Plain</enum>
+          </property>
+          <property name="alternatingRowColors">
+           <bool>true</bool>
+          </property>
+          <property name="showGrid">
+           <bool>false</bool>
+          </property>
+          <property name="sortingEnabled">
+           <bool>true</bool>
+          </property>
+          <attribute name="horizontalHeaderStretchLastSection">
+           <bool>true</bool>
+          </attribute>
+          <attribute name="verticalHeaderVisible">
+           <bool>false</bool>
+          </attribute>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="tabSetting">
+       <attribute name="title">
+        <string>Settings</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_4">
+        <property name="leftMargin">
+         <number>9</number>
+        </property>
+        <property name="topMargin">
+         <number>9</number>
+        </property>
+        <property name="rightMargin">
+         <number>9</number>
+        </property>
+        <property name="bottomMargin">
+         <number>9</number>
+        </property>
+        <item row="9" column="0">
+         <widget class="QLabel" name="labelPassword">
+          <property name="text">
+           <string>Password:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="9" column="1" colspan="2">
+         <widget class="QLineEdit" name="wifiPassword"/>
+        </item>
+        <item row="1" column="0">
+         <widget class="QLabel" name="labelNetworkBroker">
+          <property name="text">
+           <string>Network Broker:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="1" colspan="2">
+         <widget class="QLineEdit" name="localBroker">
+          <property name="placeholderText">
+           <string>URL or IP</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="1" colspan="2">
+         <widget class="QLineEdit" name="networkBroker">
+          <property name="placeholderText">
+           <string>URL or IP</string>
+          </property>
+         </widget>
+        </item>
+        <item row="8" column="0">
+         <widget class="QLabel" name="labelSSID">
+          <property name="text">
+           <string>SSID:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="1" colspan="2">
+         <widget class="QLineEdit" name="localPort">
+          <property name="placeholderText">
+           <string>Port</string>
+          </property>
+         </widget>
+        </item>
+        <item row="8" column="1" colspan="2">
+         <widget class="QLineEdit" name="wifiSSID"/>
+        </item>
+        <item row="7" column="0">
+         <widget class="QLabel" name="labelAPSelector">
+          <property name="text">
+           <string>Wi-Fi Access Point:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="1" colspan="2">
+         <widget class="QLineEdit" name="networkPort">
+          <property name="text">
+           <string/>
+          </property>
+          <property name="placeholderText">
+           <string>Port</string>
+          </property>
+         </widget>
+        </item>
+        <item row="7" column="1">
+         <widget class="QSpinBox" name="selectAP">
+          <property name="suffix">
+           <string/>
+          </property>
+          <property name="prefix">
+           <string>AP </string>
+          </property>
+          <property name="minimum">
+           <number>1</number>
+          </property>
+          <property name="maximum">
+           <number>5</number>
+          </property>
+         </widget>
+        </item>
+        <item row="6" column="0" colspan="2">
+         <spacer name="verticalSpacer">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="3" column="0">
+         <widget class="QLabel" name="labelLocalBroker">
+          <property name="text">
+           <string>Local Broker:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="7" column="2">
+         <spacer name="horizontalSpacer_3">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="10" column="2">
+         <widget class="QPushButton" name="buttonTrainDevice">
+          <property name="text">
+           <string>Train IdeasX Device</string>
+          </property>
+         </widget>
+        </item>
+        <item row="5" column="2">
+         <widget class="QPushButton" name="buttonSettings">
+          <property name="text">
+           <string>Apply Settings</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QStatusBar" name="statusbar">
+   <property name="enabled">
+    <bool>true</bool>
+   </property>
+   <property name="toolTipDuration">
+    <number>-7</number>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::RightToLeft</enum>
+   </property>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 306 - 0
WorkstationClientClass.py

@@ -0,0 +1,306 @@
+'''
+Title: IdeasXDatabaseManager Class
+Author: Tyler Berezowsky 
+Description: 
+
+This class requires the following functionality: 
+
+1) Connect to the IdeasX system (MQTT Client) 
+    - Connect using the devices MAC Address as the Client ID 
+    - Autoreconnect if the device failts 
+    - The abililty to start a broker in a seperate thread if no broker is available 
+    - The ability to store settings in a SQLite File or setting .txt file. 
+2) The ability to induce a system wide keypress in the following systems: 
+    - Windows 
+    - Mac 
+    - Linux
+3) Create a table in memory of the IdeasX devices currently in the system
+4) Parse IdeasX messages types given nothing more than a protofile 
+5) Subscribe to IdeasX devices 
+6) Invoke keystrokes if proper messages in a command is sent. 
+'''
+import sys
+import time
+import collections
+from ParsingToolsClass import ParsingTools
+
+try:
+    import paho.mqtt.client as mqtt
+    import paho.mqtt.publish as mqtt_pub
+except ImportError:
+    # This part is only required to run the example from within the examples
+    # directory when the module itself is not installed.
+    #
+    # If you have the module installed, just use "import paho.mqtt.client"
+    import os
+    import inspect
+    cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"../src")))
+    if cmd_subfolder not in sys.path:
+        sys.path.insert(0, cmd_subfolder)
+    import paho.mqtt.client as mqtt
+
+try:     
+    from protocolbuffers import IdeasXMessages_pb2 as IdeasXMessages
+except ImportError: 
+    print("The python classes for IdeasX are missing. Try running the Makefile in" +
+            "ideasX-messages.")
+    
+class WorkstationClientClass(): 
+    def __init__(self, settingFile=None, clientID = None, debug=True, mqttdebug=True):
+        # Private Class Flags and Variables
+        self.__clientID = clientID
+        self.__settingFile = settingFile
+        self.__debug = debug 
+        self.__mqttDebug = mqttdebug 
+        self.__errorIndex = 0 
+        self.__refreshCb = None
+        
+        
+        # MQTT Topics 
+        self.__DEVICETYPE = ["/encoder/+"]
+        self.__COMMANDTOPIC = "/command"
+        self.__DATATOPIC = "/data"
+        self.__HEALTHTOPIC = "/health"
+                
+        # Data Structure for Encoders / Actuators 
+        self.encoders = {}
+        self.subscribedEncoders = []
+        
+        # IdeasX Parsers
+        self._healthParser = IdeasXMessages.HealthMessage()
+        self._dataParser = IdeasXMessages.DataMessage()
+        self._commandParser = IdeasXMessages.CommandMessage()
+        self._parserTools = ParsingTools()
+
+        # MQTT Client Object
+        self._mqttc = mqtt.Client(self.__clientID, clean_session=True, userdata=None, protocol='MQTTv311')
+        
+        # Setup Callback Functions for each device type
+        for device in self.__DEVICETYPE:
+            self._mqttc.message_callback_add(device+self.__HEALTHTOPIC, self.mqtt_on_health)
+            self._mqttc.message_callback_add(device+self.__DATATOPIC, self.mqtt_on_data)
+            #self._mqttc.message_callback_add(device+self.__COMMANDTOPIC, self.mqtt_on_command)       
+        
+        self._mqttc.on_connect = self.mqtt_on_connect
+        self._mqttc.on_disconnect = self.mqtt_on_disconnect 
+        #self._mqttc.on_subscribe = self.mqtt_on_subscribe 
+        #self._mqttc.on_unsubscribe = self.mqtt_on_unsubscribe
+        
+        if self.__mqttDebug: 
+            self._mqttc.on_log = self.mqtt_on_log 
+
+#------------------------------------------------------------------------------
+# callback functions
+
+    def mqtt_on_connect(self, mqttc, backend_data, flags, rc): 
+        if rc == 0: 
+            self.printInfo('Connected to %s: %s' % (mqttc._host, mqttc._port))
+        else: 
+            self.printInfo('rc: ' + str(rc))
+        self.printLine()
+
+    def mqtt_on_disconnect(self, mqttc, backend_data, rc):
+        if self.__debug: 
+            if rc != 0: 
+                self.printError("Client disconnected and its a mystery why!")
+            else: 
+                self.printInfo("Client successfully disconnected.") 
+            self.printLine()   
+            
+    def mqtt_on_log(self, mqttc, backend_data, level, string):
+        print(string)
+        self.printLine()         
+                        
+    def mqtt_on_data(self, mqttc, backend_data, msg):
+        self.printInfo("Data Message")
+        self.printLine()
+        try: 
+            self._dataParser.ParseFromString(msg.payload)
+            print("GPIO States: " + bin(self._dataParser.button))
+        except: 
+            self.printError("Failure to parse message")
+            if self.__debug:
+                print("Raw Message: %s" %msg.payload)
+            self.printLine()
+            
+    def mqtt_on_health(self, mqttc, backend_data, msg):
+        self.printInfo("Health Message")
+        self.printLine()
+        try: 
+            self._healthParser.ParseFromString(msg.payload)
+            macID = self._parserTools.macToString(self._healthParser.module_id)
+            
+            if self._healthParser.alive:
+                temp_list = []
+                for field in self._healthParser.ListFields():
+                    temp_list.append((field[0].name, field[1]))  
+                temp_list.append(('time', time.time()))          
+                self.encoders[macID] = collections.OrderedDict(temp_list)
+            else:
+                try: 
+                    self.encoders.pop(macID)
+                except KeyError: 
+                    self.printError("Encoder ID " +macID+" is not stored")
+            
+            if self.__refreshCb: 
+                print("FUUUUUUUUUUUUUUUUUCK")
+                self.__refreshCb()
+            
+            if self.__debug:
+                for encoder, fields in zip(self.encoders.keys(), self.encoders.values()): 
+                    print(str(encoder) +" : "+ str(fields))
+                self.printLine()
+        except: 
+            self.printError("Error: Failure to parse message")
+            if self.__debug:
+                print("Raw Message: %s" %msg.payload)
+            self.printLine()
+        
+
+
+        
+
+#----------------------------------------------thy--------------------------------
+# General API Calls 
+        
+    def cmdStartWorkstationClient(self, ip="server.ideasX.tech", port=1883, keepAlive=60):     
+        self.ip = ip 
+        self.port = port 
+        self.keepAlive = keepAlive 
+        
+        self.printLine()
+        self.printInfo("Starting Workstation Client (WSC)")
+        self.printLine()
+        
+        try:  
+            self._mqttc.connect(self.ip, self.port, self.keepAlive)      # connect to broker
+            
+            for device in self.__DEVICETYPE:
+                self._mqttc.subscribe(device + self.__HEALTHTOPIC, 1)
+            self._mqttc.loop_forever() # need to use blocking loop otherwise python will kill process
+        except:
+            self.printError("There was a fucking mistake here.")
+            sys.exit(1)
+            
+    def guiStartWorkstationClient(self, ip="server.ideasx.tech", port=1883, keepAlive=60):
+        self.ip = ip 
+        self.port = port 
+        self.keepAlive = keepAlive
+        
+        self.printLine()
+        self.printInfo("Starting Workstation Client (WSC)")
+        self.printLine()
+        
+        try: 
+            self._mqttc.connect(self.ip, self.port, self.keepAlive)
+            for device in self.__DEVICETYPE:
+                self._mqttc.subscribe(device + self.__HEALTHTOPIC, 0)
+                self._mqttc.subscribe(device + self.__DATATOPIC, 0)
+            self._mqttc.loop_start() # start MQTT Client Thread 
+        except: 
+            self.printError("There was a fucking mistake here.")
+            sys.exit(1)
+            
+            
+    def restartWSC(self):
+        self.printInfo("This really doesn't do anything")
+    
+    def killWSC(self):
+        self._mqttc.loop_stop()
+        self.printInfo("Murdered MQTT thread.")
+        
+    def attachRefreshCallback(self, cb):
+        self.__refreshCb = cb
+        
+            
+    def activateEncoder(self, deviceMACAddress, deviceType=None):
+        '''
+        Subscribe to device's data topic and send activate command if device 
+        is not active. 
+        
+        * Currently does not confirm subscribe is successful 
+        * Currently does not send the activate command as it does not exist
+        
+        deviceType = str 
+        deviceMACAddress = str(MAC_ID)
+        '''
+        if deviceMACAddress in self.encoders.keys():
+            if deviceType == None: 
+                deviceDataTopic = self.__DEVICETYPE[0] + deviceMACAddress + self.__DATATOPIC
+            else: 
+                deviceDataTopic = deviceType + deviceMACAddress + self.__DATATOPIC
+                 
+            self._mqttc.subscribe(deviceDataTopic, 1)
+            self.subscribedEncoders.append(deviceMACAddress)
+            if self.__debug: 
+                self.printInfo("Device " + deviceMACAddress + " data topic was subscribed")
+        else: 
+            self.printError("Device " + deviceMACAddress + " is not currently in the IdeasX system.")
+            
+    def deactivateEncoder(self, deviceMACAddress, deviceType=None, forceAction=False):
+        '''
+        Unsubscribe from device's data topic and send deactive command if no other WSC are using device. 
+        
+        * Currently does not confirm unsubscribe is successful 
+        * Currently does not send the deactive command as it does not exist and I don't know how to sync that shit. 
+        '''
+        if (deviceMACAddress in self.encoders.keys()) or (forceAction): 
+            if deviceType == None: 
+                deviceDataTopic = self.__DEVICETYPE[0] + deviceMACAddress + self.__DATATOPIC
+            else: 
+                deviceDataTopic = deviceType + deviceMACAddress + self.__DATATOPIC 
+            self._mqttc.unsubscribe(deviceDataTopic)
+            self.subscribedEncoders.remove(deviceMACAddress)
+            if self.__debug: 
+                self.printInfo("Device " + deviceMACAddress + " data topic was unsubscribed")
+        else: 
+            self.printError("Device " + deviceMACAddress + " is not currently in the IdeasX System")    
+
+
+    def shutdownDevice(self, deviceMACAddress, deviceType=None):
+        self._commandParser.command = self._commandParser.SHUT_DOWN
+        self._mqttc.publish(self.__DEVICETYPE[0][:-1] + deviceMACAddress + self.__COMMANDTOPIC,
+                            self._commandParser.SerializeToString().decode('utf-8') ,
+                            qos=1,
+                            retain=False)
+        self.printInfo("Send Shutdown Command to Encoder " + deviceMACAddress)
+        
+    def printLine(self):
+        print('-'*70)
+        
+    def printError(self, errorStr):
+        self.__errorIndex = self.__errorIndex + 1
+        print("WSC Error #" + str(self.__errorIndex) + ": " + errorStr)
+    
+    def printInfo(self, msgStr):
+        print("WSC: " + msgStr)
+        
+
+if __name__ == "__main__": 
+    Host = "ideasx.duckdns.org"
+    Host = "192.168.0.101"
+    Host = "10.42.0.1"
+    Port = 1883 
+    KeepAlive = 30
+    msgFlag = False;     
+    deviceID = None; 
+    cmdPayload = None; 
+    cmdArg = None;
+    cmdTest = False; 
+    
+    
+    wsc = WorkstationClientClass()
+    
+    if cmdTest: 
+        wsc.cmdStartWorkstationClient(Host, Port, KeepAlive)
+    else: 
+        wsc.guiStartWorkstationClient(Host, Port, KeepAlive)
+        time.sleep(3)
+        wsc.activateEncoder('18:fe:34:f1:f2:8d')
+        print(wsc.subscribedEncoders)
+        time.sleep(2)
+        wsc.deactivateEncoder('18:fe:34:f1:f2:8d')
+        print(wsc.subscribedEncoders)
+        time.sleep(10)
+        wsc.killWSC()
+        

BIN
icon/devicetype/modulev3.png


BIN
icon/ideasx.png


+ 58 - 34
ideasxdevice.py

@@ -11,22 +11,36 @@ from PyQt5 import QtCore, QtGui, QtWidgets
 class Ui_IdeasXDevice(object):
     def setupUi(self, IdeasXDevice):
         IdeasXDevice.setObjectName("IdeasXDevice")
-        IdeasXDevice.resize(416, 70)
-        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+        IdeasXDevice.resize(506, 84)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
         sizePolicy.setHorizontalStretch(0)
         sizePolicy.setVerticalStretch(0)
         sizePolicy.setHeightForWidth(IdeasXDevice.sizePolicy().hasHeightForWidth())
         IdeasXDevice.setSizePolicy(sizePolicy)
-        IdeasXDevice.setMinimumSize(QtCore.QSize(416, 70))
+        IdeasXDevice.setMinimumSize(QtCore.QSize(452, 84))
+        IdeasXDevice.setMaximumSize(QtCore.QSize(16777215, 84))
+        IdeasXDevice.setAcceptDrops(False)
         IdeasXDevice.setAutoFillBackground(False)
-        self.buttonActivate = QtWidgets.QPushButton(IdeasXDevice)
-        self.buttonActivate.setGeometry(QtCore.QRect(289, 11, 121, 31))
-        self.buttonActivate.setAutoFillBackground(False)
-        self.buttonActivate.setCheckable(True)
-        self.buttonActivate.setChecked(False)
-        self.buttonActivate.setObjectName("buttonActivate")
-        self.labelModuleID = QtWidgets.QLabel(IdeasXDevice)
-        self.labelModuleID.setGeometry(QtCore.QRect(110, 10, 231, 31))
+        self.horizontalLayout = QtWidgets.QHBoxLayout(IdeasXDevice)
+        self.horizontalLayout.setObjectName("horizontalLayout")
+        self.widgetInfo = QtWidgets.QWidget(IdeasXDevice)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.widgetInfo.sizePolicy().hasHeightForWidth())
+        self.widgetInfo.setSizePolicy(sizePolicy)
+        self.widgetInfo.setMinimumSize(QtCore.QSize(290, 66))
+        self.widgetInfo.setObjectName("widgetInfo")
+        self.label = QtWidgets.QLabel(self.widgetInfo)
+        self.label.setGeometry(QtCore.QRect(4, 5, 90, 60))
+        self.label.setText("")
+        self.label.setPixmap(QtGui.QPixmap("./icon/devicetype/modulev3.png"))
+        self.label.setObjectName("label")
+        self.labelRSSI = QtWidgets.QLabel(self.widgetInfo)
+        self.labelRSSI.setGeometry(QtCore.QRect(100, 50, 86, 16))
+        self.labelRSSI.setObjectName("labelRSSI")
+        self.labelModuleID = QtWidgets.QLabel(self.widgetInfo)
+        self.labelModuleID.setGeometry(QtCore.QRect(100, 0, 231, 35))
         font = QtGui.QFont()
         font.setFamily("Roboto Condensed")
         font.setPointSize(20)
@@ -34,31 +48,40 @@ class Ui_IdeasXDevice(object):
         self.labelModuleID.setFont(font)
         self.labelModuleID.setFrameShape(QtWidgets.QFrame.NoFrame)
         self.labelModuleID.setObjectName("labelModuleID")
-        self.labelStatus = QtWidgets.QLabel(IdeasXDevice)
-        self.labelStatus.setGeometry(QtCore.QRect(280, 50, 127, 16))
+        self.labelBattery = QtWidgets.QLabel(self.widgetInfo)
+        self.labelBattery.setGeometry(QtCore.QRect(100, 35, 166, 16))
+        self.labelBattery.setObjectName("labelBattery")
+        self.horizontalLayout.addWidget(self.widgetInfo)
+        spacerItem = QtWidgets.QSpacerItem(10, 20, QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout.addItem(spacerItem)
+        self.widgetControls = QtWidgets.QWidget(IdeasXDevice)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.widgetControls.sizePolicy().hasHeightForWidth())
+        self.widgetControls.setSizePolicy(sizePolicy)
+        self.widgetControls.setMinimumSize(QtCore.QSize(120, 66))
+        self.widgetControls.setMaximumSize(QtCore.QSize(120, 66))
+        self.widgetControls.setObjectName("widgetControls")
+        self.labelStatus = QtWidgets.QLabel(self.widgetControls)
+        self.labelStatus.setGeometry(QtCore.QRect(-5, 55, 116, 10))
         font = QtGui.QFont()
-        font.setFamily("DejaVu Sans")
         font.setPointSize(6)
         font.setItalic(True)
         self.labelStatus.setFont(font)
         self.labelStatus.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
         self.labelStatus.setObjectName("labelStatus")
-        self.labelBattery = QtWidgets.QLabel(IdeasXDevice)
-        self.labelBattery.setGeometry(QtCore.QRect(110, 40, 91, 16))
-        self.labelBattery.setObjectName("labelBattery")
-        self.labelRSSI = QtWidgets.QLabel(IdeasXDevice)
-        self.labelRSSI.setGeometry(QtCore.QRect(110, 50, 91, 16))
-        self.labelRSSI.setObjectName("labelRSSI")
-        self.label = QtWidgets.QLabel(IdeasXDevice)
-        self.label.setGeometry(QtCore.QRect(10, 0, 91, 71))
-        self.label.setText("")
-        self.label.setPixmap(QtGui.QPixmap("icon/devicetype/modulev3.png"))
-        self.label.setObjectName("label")
-        self.line = QtWidgets.QFrame(IdeasXDevice)
-        self.line.setGeometry(QtCore.QRect(-13, 60, 431, 20))
-        self.line.setFrameShape(QtWidgets.QFrame.HLine)
-        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        self.line.setObjectName("line")
+        self.buttonMenu = QtWidgets.QToolButton(self.widgetControls)
+        self.buttonMenu.setGeometry(QtCore.QRect(95, 10, 21, 36))
+        self.buttonMenu.setAutoRaise(False)
+        self.buttonMenu.setArrowType(QtCore.Qt.DownArrow)
+        self.buttonMenu.setObjectName("buttonMenu")
+        self.buttonActivate = QtWidgets.QToolButton(self.widgetControls)
+        self.buttonActivate.setGeometry(QtCore.QRect(5, 10, 91, 36))
+        self.buttonActivate.setCheckable(True)
+        self.buttonActivate.setAutoRaise(False)
+        self.buttonActivate.setObjectName("buttonActivate")
+        self.horizontalLayout.addWidget(self.widgetControls)
 
         self.retranslateUi(IdeasXDevice)
         QtCore.QMetaObject.connectSlotsByName(IdeasXDevice)
@@ -66,9 +89,10 @@ class Ui_IdeasXDevice(object):
     def retranslateUi(self, IdeasXDevice):
         _translate = QtCore.QCoreApplication.translate
         IdeasXDevice.setWindowTitle(_translate("IdeasXDevice", "Form"))
-        self.buttonActivate.setText(_translate("IdeasXDevice", "Activate"))
-        self.labelModuleID.setText(_translate("IdeasXDevice", "2f:32:56:4f:72"))
-        self.labelStatus.setText(_translate("IdeasXDevice", "Last Update: 7:46AM"))
-        self.labelBattery.setText(_translate("IdeasXDevice", "Battery:  98%"))
         self.labelRSSI.setText(_translate("IdeasXDevice", "RSSI:  -54dBm"))
+        self.labelModuleID.setText(_translate("IdeasXDevice", "2f:32:56:4f:72:d8"))
+        self.labelBattery.setText(_translate("IdeasXDevice", "Battery:  98%"))
+        self.labelStatus.setText(_translate("IdeasXDevice", "Last Update: 2:30AM"))
+        self.buttonMenu.setText(_translate("IdeasXDevice", "Activate "))
+        self.buttonActivate.setText(_translate("IdeasXDevice", "Activate"))
 

+ 74 - 0
ideasxdevice2.py

@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'ideasxdevice.ui'
+#
+# Created by: PyQt5 UI code generator 5.7
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+class Ui_IdeasXDevice(object):
+    def setupUi(self, IdeasXDevice):
+        IdeasXDevice.setObjectName("IdeasXDevice")
+        IdeasXDevice.resize(416, 70)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(IdeasXDevice.sizePolicy().hasHeightForWidth())
+        IdeasXDevice.setSizePolicy(sizePolicy)
+        IdeasXDevice.setMinimumSize(QtCore.QSize(416, 70))
+        IdeasXDevice.setAutoFillBackground(False)
+        self.buttonActivate = QtWidgets.QPushButton(IdeasXDevice)
+        self.buttonActivate.setGeometry(QtCore.QRect(289, 11, 121, 31))
+        self.buttonActivate.setAutoFillBackground(False)
+        self.buttonActivate.setCheckable(True)
+        self.buttonActivate.setChecked(False)
+        self.buttonActivate.setObjectName("buttonActivate")
+        self.labelModuleID = QtWidgets.QLabel(IdeasXDevice)
+        self.labelModuleID.setGeometry(QtCore.QRect(105, 5, 181, 31))
+        font = QtGui.QFont()
+        font.setFamily("Roboto Condensed")
+        font.setPointSize(20)
+        font.setItalic(False)
+        self.labelModuleID.setFont(font)
+        self.labelModuleID.setFrameShape(QtWidgets.QFrame.NoFrame)
+        self.labelModuleID.setObjectName("labelModuleID")
+        self.labelStatus = QtWidgets.QLabel(IdeasXDevice)
+        self.labelStatus.setGeometry(QtCore.QRect(226, 50, 181, 16))
+        font = QtGui.QFont()
+        font.setFamily("DejaVu Sans")
+        font.setPointSize(6)
+        font.setItalic(True)
+        self.labelStatus.setFont(font)
+        self.labelStatus.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+        self.labelStatus.setObjectName("labelStatus")
+        self.labelBattery = QtWidgets.QLabel(IdeasXDevice)
+        self.labelBattery.setGeometry(QtCore.QRect(105, 35, 171, 16))
+        self.labelBattery.setObjectName("labelBattery")
+        self.labelRSSI = QtWidgets.QLabel(IdeasXDevice)
+        self.labelRSSI.setGeometry(QtCore.QRect(105, 50, 91, 16))
+        self.labelRSSI.setObjectName("labelRSSI")
+        self.label = QtWidgets.QLabel(IdeasXDevice)
+        self.label.setGeometry(QtCore.QRect(10, 0, 91, 71))
+        self.label.setText("")
+        self.label.setPixmap(QtGui.QPixmap("./icon/devicetype/modulev3.png"))
+        self.label.setObjectName("label")
+        self.line = QtWidgets.QFrame(IdeasXDevice)
+        self.line.setGeometry(QtCore.QRect(-13, 60, 431, 20))
+        self.line.setFrameShape(QtWidgets.QFrame.HLine)
+        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        self.line.setObjectName("line")
+
+        self.retranslateUi(IdeasXDevice)
+        QtCore.QMetaObject.connectSlotsByName(IdeasXDevice)
+
+    def retranslateUi(self, IdeasXDevice):
+        _translate = QtCore.QCoreApplication.translate
+        IdeasXDevice.setWindowTitle(_translate("IdeasXDevice", "Form"))
+        self.buttonActivate.setText(_translate("IdeasXDevice", "Activate"))
+        self.labelModuleID.setText(_translate("IdeasXDevice", "2f:32:56:4f:72"))
+        self.labelStatus.setText(_translate("IdeasXDevice", "Last Update: 7:46AM"))
+        self.labelBattery.setText(_translate("IdeasXDevice", "Battery:  98%"))
+        self.labelRSSI.setText(_translate("IdeasXDevice", "RSSI:  -54dBm"))
+

+ 9 - 5
mainwindow.py

@@ -11,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
 class Ui_MainWindow(object):
     def setupUi(self, MainWindow):
         MainWindow.setObjectName("MainWindow")
-        MainWindow.resize(480, 556)
+        MainWindow.resize(525, 648)
         sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
         sizePolicy.setHorizontalStretch(0)
         sizePolicy.setVerticalStretch(0)
@@ -29,6 +29,8 @@ class Ui_MainWindow(object):
         self.centralwidget.setSizePolicy(sizePolicy)
         self.centralwidget.setObjectName("centralwidget")
         self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
+        self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
+        self.verticalLayout.setSpacing(6)
         self.verticalLayout.setObjectName("verticalLayout")
         self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
         self.tabWidget.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
@@ -43,7 +45,8 @@ class Ui_MainWindow(object):
         self.tabEncoder.setMaximumSize(QtCore.QSize(16777215, 16777215))
         self.tabEncoder.setObjectName("tabEncoder")
         self.gridLayout = QtWidgets.QGridLayout(self.tabEncoder)
-        self.gridLayout.setContentsMargins(0, 0, 0, 0)
+        self.gridLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
+        self.gridLayout.setContentsMargins(9, 9, 9, 9)
         self.gridLayout.setObjectName("gridLayout")
         self.searchEncoder = QtWidgets.QLineEdit(self.tabEncoder)
         self.searchEncoder.setClearButtonEnabled(True)
@@ -57,13 +60,14 @@ class Ui_MainWindow(object):
         sizePolicy.setVerticalStretch(0)
         sizePolicy.setHeightForWidth(self.listEncoder.sizePolicy().hasHeightForWidth())
         self.listEncoder.setSizePolicy(sizePolicy)
+        self.listEncoder.setAlternatingRowColors(True)
         self.listEncoder.setObjectName("listEncoder")
         self.gridLayout.addWidget(self.listEncoder, 0, 0, 1, 2)
         self.tabWidget.addTab(self.tabEncoder, "")
         self.tabActuator = QtWidgets.QWidget()
         self.tabActuator.setObjectName("tabActuator")
         self.gridLayout_2 = QtWidgets.QGridLayout(self.tabActuator)
-        self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
+        self.gridLayout_2.setContentsMargins(9, 9, 9, 9)
         self.gridLayout_2.setObjectName("gridLayout_2")
         self.searchActuator = QtWidgets.QLineEdit(self.tabActuator)
         self.searchActuator.setAutoFillBackground(False)
@@ -87,7 +91,7 @@ class Ui_MainWindow(object):
         self.tabSetting = QtWidgets.QWidget()
         self.tabSetting.setObjectName("tabSetting")
         self.gridLayout_4 = QtWidgets.QGridLayout(self.tabSetting)
-        self.gridLayout_4.setContentsMargins(0, 0, 0, 0)
+        self.gridLayout_4.setContentsMargins(9, 9, 9, 9)
         self.gridLayout_4.setObjectName("gridLayout_4")
         self.labelPassword = QtWidgets.QLabel(self.tabSetting)
         self.labelPassword.setObjectName("labelPassword")
@@ -156,7 +160,7 @@ class Ui_MainWindow(object):
     def retranslateUi(self, MainWindow):
         _translate = QtCore.QCoreApplication.translate
         MainWindow.setWindowTitle(_translate("MainWindow", "IdeasX Workstation Client"))
-        self.searchEncoder.setPlaceholderText(_translate("MainWindow", "Search for Encoder"))
+        self.searchEncoder.setPlaceholderText(_translate("MainWindow", "Search for Encoders..."))
         self.listEncoder.setSortingEnabled(False)
         self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabEncoder), _translate("MainWindow", "Encoders"))
         self.searchActuator.setPlaceholderText(_translate("MainWindow", "Search for Actuator"))

+ 0 - 1
protocolbuffers/IdeasXMessages_pb2.py

@@ -1 +0,0 @@
-/home/tyler/ideasX-repositories/ideasX-messages/Python/IdeasXMessages_pb2.py

+ 358 - 0
protocolbuffers/IdeasXMessages_pb2.py

@@ -0,0 +1,358 @@
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: IdeasXMessages.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='IdeasXMessages.proto',
+  package='',
+  syntax='proto2',
+  serialized_pb=_b('\n\x14IdeasXMessages.proto\"\xb1\x03\n\rHealthMessage\x12\x11\n\tmodule_id\x18\x01 \x02(\x0c\x12\r\n\x05\x61live\x18\x02 \x02(\x08\x12\x0e\n\x06\x61\x63tive\x18\x03 \x01(\x08\x12\n\n\x02lb\x18\x04 \x02(\x08\x12\x0b\n\x03ota\x18\x05 \x02(\x08\x12\x10\n\x08\x63harging\x18\x06 \x01(\x08\x12\x0b\n\x03soc\x18\x07 \x01(\x05\x12\r\n\x05vcell\x18\x08 \x02(\x05\x12\x1f\n\x03rom\x18\t \x02(\x0e\x32\x12.HealthMessage.Rom\x12\x18\n\x10\x66irmware_version\x18\n \x02(\x05\x12\x18\n\x10hardware_version\x18\x0b \x02(\x05\x12\x0c\n\x04rssi\x18\x0c \x02(\x05\x12\x0c\n\x04ssid\x18\r \x02(\t\x12\r\n\x05\x62ssid\x18\x0e \x01(\t\x12!\n\x04\x61uth\x18\x0f \x02(\x0e\x32\x13.HealthMessage.Auth\"#\n\x03Rom\x12\x08\n\x04ROM0\x10\x00\x12\x08\n\x04ROM1\x10\x01\x12\x08\n\x04ROM2\x10\x02\"_\n\x04\x41uth\x12\r\n\tAUTH_OPEN\x10\x00\x12\x0c\n\x08\x41UTH_WEP\x10\x01\x12\x10\n\x0c\x41UTH_WPA_PSK\x10\x02\x12\x11\n\rAUTH_WPA2_PSK\x10\x03\x12\x15\n\x11\x41UTH_WPA_WPA2_PSK\x10\x04\"2\n\x0b\x44\x61taMessage\x12\x0e\n\x06\x62utton\x18\x01 \x02(\x05\x12\x13\n\x0bimu_samples\x18\x02 \x01(\x0c\"\x96\x01\n\x0e\x43ommandMessage\x12(\n\x07\x63ommand\x18\x01 \x02(\x0e\x32\x17.CommandMessage.Command\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"I\n\x07\x43ommand\x12\x0e\n\nOTA_UPDATE\x10\x00\x12\r\n\tSHUT_DOWN\x10\x01\x12\x0b\n\x07UART_TX\x10\x02\x12\x12\n\x0eLSM6DS3_CONFIG\x10\x03')
+)
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+
+
+_HEALTHMESSAGE_ROM = _descriptor.EnumDescriptor(
+  name='Rom',
+  full_name='HealthMessage.Rom',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='ROM0', index=0, number=0,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='ROM1', index=1, number=1,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='ROM2', index=2, number=2,
+      options=None,
+      type=None),
+  ],
+  containing_type=None,
+  options=None,
+  serialized_start=326,
+  serialized_end=361,
+)
+_sym_db.RegisterEnumDescriptor(_HEALTHMESSAGE_ROM)
+
+_HEALTHMESSAGE_AUTH = _descriptor.EnumDescriptor(
+  name='Auth',
+  full_name='HealthMessage.Auth',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='AUTH_OPEN', index=0, number=0,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='AUTH_WEP', index=1, number=1,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='AUTH_WPA_PSK', index=2, number=2,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='AUTH_WPA2_PSK', index=3, number=3,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='AUTH_WPA_WPA2_PSK', index=4, number=4,
+      options=None,
+      type=None),
+  ],
+  containing_type=None,
+  options=None,
+  serialized_start=363,
+  serialized_end=458,
+)
+_sym_db.RegisterEnumDescriptor(_HEALTHMESSAGE_AUTH)
+
+_COMMANDMESSAGE_COMMAND = _descriptor.EnumDescriptor(
+  name='Command',
+  full_name='CommandMessage.Command',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='OTA_UPDATE', index=0, number=0,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='SHUT_DOWN', index=1, number=1,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='UART_TX', index=2, number=2,
+      options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='LSM6DS3_CONFIG', index=3, number=3,
+      options=None,
+      type=None),
+  ],
+  containing_type=None,
+  options=None,
+  serialized_start=590,
+  serialized_end=663,
+)
+_sym_db.RegisterEnumDescriptor(_COMMANDMESSAGE_COMMAND)
+
+
+_HEALTHMESSAGE = _descriptor.Descriptor(
+  name='HealthMessage',
+  full_name='HealthMessage',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='module_id', full_name='HealthMessage.module_id', index=0,
+      number=1, type=12, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='alive', full_name='HealthMessage.alive', index=1,
+      number=2, type=8, cpp_type=7, label=2,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='active', full_name='HealthMessage.active', index=2,
+      number=3, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='lb', full_name='HealthMessage.lb', index=3,
+      number=4, type=8, cpp_type=7, label=2,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='ota', full_name='HealthMessage.ota', index=4,
+      number=5, type=8, cpp_type=7, label=2,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='charging', full_name='HealthMessage.charging', index=5,
+      number=6, type=8, cpp_type=7, label=1,
+      has_default_value=False, default_value=False,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='soc', full_name='HealthMessage.soc', index=6,
+      number=7, type=5, cpp_type=1, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='vcell', full_name='HealthMessage.vcell', index=7,
+      number=8, type=5, cpp_type=1, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='rom', full_name='HealthMessage.rom', index=8,
+      number=9, type=14, cpp_type=8, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='firmware_version', full_name='HealthMessage.firmware_version', index=9,
+      number=10, type=5, cpp_type=1, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='hardware_version', full_name='HealthMessage.hardware_version', index=10,
+      number=11, type=5, cpp_type=1, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='rssi', full_name='HealthMessage.rssi', index=11,
+      number=12, type=5, cpp_type=1, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='ssid', full_name='HealthMessage.ssid', index=12,
+      number=13, type=9, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='bssid', full_name='HealthMessage.bssid', index=13,
+      number=14, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='auth', full_name='HealthMessage.auth', index=14,
+      number=15, type=14, cpp_type=8, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+    _HEALTHMESSAGE_ROM,
+    _HEALTHMESSAGE_AUTH,
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=25,
+  serialized_end=458,
+)
+
+
+_DATAMESSAGE = _descriptor.Descriptor(
+  name='DataMessage',
+  full_name='DataMessage',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='button', full_name='DataMessage.button', index=0,
+      number=1, type=5, cpp_type=1, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='imu_samples', full_name='DataMessage.imu_samples', index=1,
+      number=2, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=460,
+  serialized_end=510,
+)
+
+
+_COMMANDMESSAGE = _descriptor.Descriptor(
+  name='CommandMessage',
+  full_name='CommandMessage',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='command', full_name='CommandMessage.command', index=0,
+      number=1, type=14, cpp_type=8, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='payload', full_name='CommandMessage.payload', index=1,
+      number=2, type=12, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+    _COMMANDMESSAGE_COMMAND,
+  ],
+  options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=513,
+  serialized_end=663,
+)
+
+_HEALTHMESSAGE.fields_by_name['rom'].enum_type = _HEALTHMESSAGE_ROM
+_HEALTHMESSAGE.fields_by_name['auth'].enum_type = _HEALTHMESSAGE_AUTH
+_HEALTHMESSAGE_ROM.containing_type = _HEALTHMESSAGE
+_HEALTHMESSAGE_AUTH.containing_type = _HEALTHMESSAGE
+_COMMANDMESSAGE.fields_by_name['command'].enum_type = _COMMANDMESSAGE_COMMAND
+_COMMANDMESSAGE_COMMAND.containing_type = _COMMANDMESSAGE
+DESCRIPTOR.message_types_by_name['HealthMessage'] = _HEALTHMESSAGE
+DESCRIPTOR.message_types_by_name['DataMessage'] = _DATAMESSAGE
+DESCRIPTOR.message_types_by_name['CommandMessage'] = _COMMANDMESSAGE
+
+HealthMessage = _reflection.GeneratedProtocolMessageType('HealthMessage', (_message.Message,), dict(
+  DESCRIPTOR = _HEALTHMESSAGE,
+  __module__ = 'IdeasXMessages_pb2'
+  # @@protoc_insertion_point(class_scope:HealthMessage)
+  ))
+_sym_db.RegisterMessage(HealthMessage)
+
+DataMessage = _reflection.GeneratedProtocolMessageType('DataMessage', (_message.Message,), dict(
+  DESCRIPTOR = _DATAMESSAGE,
+  __module__ = 'IdeasXMessages_pb2'
+  # @@protoc_insertion_point(class_scope:DataMessage)
+  ))
+_sym_db.RegisterMessage(DataMessage)
+
+CommandMessage = _reflection.GeneratedProtocolMessageType('CommandMessage', (_message.Message,), dict(
+  DESCRIPTOR = _COMMANDMESSAGE,
+  __module__ = 'IdeasXMessages_pb2'
+  # @@protoc_insertion_point(class_scope:CommandMessage)
+  ))
+_sym_db.RegisterMessage(CommandMessage)
+
+
+# @@protoc_insertion_point(module_scope)