BACKGROUND: I'm working on a plugin for serial communication/control of Zodiac/Jandy Pool/Spa systems. I assume that users will have only a single Jandy system on a given instance of Indigo, and verify that when adding a new Jandy device.
With that background, here's how I've implemented the serial communications, which need to be opened, and a concurrent thread used to send commands and receive information that the Jandy system sends, independent of user commands.
In the main python.py the following is used to start and end device communication (note that python.py also queues commands that arrive from Actions):
- Code: Select all
##############################################################################################
def deviceStartComm(self, dev):
self.debugLog("<<-- entering deviceStartComm, Device: " + dev.name + "; ID=" + str(dev.id) + ", Type=" + dev.deviceTypeId)
self.jandyaqualink.startCommThread(dev)
self.debugLog(u"exiting deviceStartComm -->>")
def deviceStopComm(self, dev):
self.debugLog("<<-- entering deviceStopComm, Device: " + dev.name + "; ID=" + str(dev.id) + ", Type=" + dev.deviceTypeId)
self.jandyaqualink.stopCommThread(dev)
self.debugLog("exiting deviceStopComm -->>")
python.py then calls the following in jandyaqualink.py to actually start and stop the serial communication, and start/stop a concurrent thread to handle ongoing communication:
- Code: Select all
##############################################################################################
def startCommThread(self, dev):
devProps = dev.pluginProps
portName = devProps.get("serialPort")
self.conn = self.plugin.openSerial(dev.name, portName, 9600, timeout=1, writeTimeout=1)
### If connection to serial device is successfully statrted, queue initial commands
### Else, log error starting communication
if self.conn:
### Queue commands here ...
self.commQueue = []
(add commands to queue)
### Start separate concurrentSerialComm thread - syntax roughly based on EasyDAQ Plugin
self.concurThread = threading.Thread(target=functools.partial(self.concurrentSerialComm, dev))
self.concurThread.start()
else:
indigo.server.log("Error initializing communciations with serial device " + dev.name)
######################
# Send command to stop concurrentSerialComm thread, then close serial connection and exit.
def stopCommThread(self, dev):
self.queueSerialCmd("stopConcurrentSerialComm")
time.sleep(10) # Give plenty of time for command to stop concurrent thread to execute.
self.conn.close()
self.plugin.debugLog("closed connection to device " + dev.name)
##############################################################################################
def concurrentSerialComm(self, dev):
try:
while True:
##################################################################################
# Start each pass by reading/decoding any commands from Aqualink.
self.readSerialBuffer(dev)
##################################################################################
# Next, send commands from those queued.
# Use a copy of the queue to avoid confusion if added to while executing.
# Check for command to terminate thread
if len(self.commQueue) >= 1:
workingQueue = self.commQueue
lenQueue = len(workingQueue)
for command in self.commQueue:
if command == "stopConcurrentSerialComm": # Command to stop queue?
raise self.StopThread # Raise exception to stop thread.
else:
self.sendSerialCmd(dev, command)
self.commQueue = self.commQueue[lenQueue:]
##################################################################################
# Sleep a bit on each cycle.
time.sleep(kSleepAfterSerialCommand * 3)
### Exceptions to exit thread. !!! Need to understand 2nd and 3rd except:, "borrowed" from EasyDAQ plugin.
except self.StopThread:
pass # silently fall into finally: section below
except Exception, e:
self.plugin.exceptionLog()
except:
self.plugin.exceptionLog()
finally:
pass # Finally, exit thread.
Does this seem reasonable? It seems to work, but I wasn't sure if passing information using the self.conn and self. commQueue are "acceptable"?
Jim