IPyDriver

Typically you would create a subclass of IPyDriver.

The driver has methods which can be overwritten.

async def rxevent(self, event)

This is called whenever data is received from the client, typically to set an instrument parameter. The event object describes the received data, and you provide the code which then controls your instrument, event objects are described at RxEvents.

async def hardware(self)

This could be a contuously running coroutine which you can use to operate your instruments, and if required send updates to the client.

async def snoopevent(self, event)

This is used if the device is monitoring the data sent by other devices, these event objects are described at SnoopEvents.

IPyDriver is a mapping of devicename:deviceobject, so your code in these methods could access a specific device using self[‘devicename’].

As well as the methods documented below, dict methods are available such as get() and iteration through keys(), values() and items().

Similarly a Device object is a mapping to a vector, so to access a vector you could use self[‘devicename’][‘vectorname’].


class IPyDriver(*devices, **driverdata)

A subclass of this should be created with methods written to control your device.

devices are Device objects this driver handles.

You may optionally include named arguments of any instrumentation objects that may be useful to you, these will be available in the dictionary self.driverdata.

This object is also a mapping, of devicename:deviceobject

add_background(coro)

This method adds a coroutine to be run in the background.

async asyncrun()

await this to operate the driver, which will then communicate by stdin and stdout.

Do not await this if the driver is being set into IPyServer, in that situation the IPyServer will control communications and will call this itself when run.

devices()

Returns a list of device objects

async hardware()

Override this to operate device hardware, and transmit updates

For example: call your own code to operate hardware then update the appropriate vectors, and send updated values to the client using await vector.send_setVector()

static indi_number_to_float(value)

The INDI spec allows a number of different number formats, given any number string, this returns a float. If an error occurs while parsing the number, a TypeError exception is raised.

async rxevent(event)

Override this. On receiving data, this is called, and should handle any necessary actions. event is an object describing the event, with attributes devicename, vectorname, vector, root where vector is the properties vector the event refers to, and root is an xml.etree.ElementTree object of the received xml

async send(xmldata)

Transmits xmldata, this is an internal method, not normally called by a user.

async send_getProperties(devicename=None, vectorname=None)

Sends a getProperties request - which is used to snoop data from other devices on the network, if devicename given, it must not be a device of this driver as the point of this is to snoop on remote devices.

async send_message(message='', timestamp=None)

Send system wide message - without device name

shutdown()

Shuts down the driver, sets the flag self.stop to True

snoop(devicename, vectorname, timeout=30)

Call this to snoop on a given devicename, vectorname. This will cause a getProperties to be sent, and will also send further getProperties every timeout seconds if no snooping data is being received from the specified vector. This avoids a possible problem where intermediate servers may be temporarily turned off, and will lose their instruction to broadcast snooping traffic. This method is only applicable when snooping on a specific device vector. timeout must be an integer equal or greater than 5 seconds.

async snoopevent(event)

Override this if this driver is snooping on other devices. On receiving snoop data, this is called, and should handle any necessary actions. event is an object with attributes according to the data received.

property stop

returns self._stop, being the instruction to stop the driver


The ipydriver object has attributes:

driverdata - The dictionary of named arguments you have optionally set in the constructor.

This is particularly useful to pass in any object which controls your instrument, and which is then accessable in your rxevent and hardware methods.

auto_send_def - As default this is True.

With auto_send_def set to True, whenever a getProperties event is received from a client, a vector send_defVector() method will be called, automatically replying with the vector definition. If set to False, the driver developer will need to test for a getProperties event in the rxevent method, and implement a send_defVector() . Possibly one reason you may want to do this is to send a message with every vector definition.

stop - Normally False, but becomes set to True when the driver shutdown() method is called.

shutdownrequested - An asyncio.Event() object, set when shutdown() is called, await driver.shutdownrequested.wait() will block until shutdown() called.

stopped - An asyncio.Event() object, await driver.stopped.wait() will block until the driver stops.

debug_enable - As default this is False.

With debug_enable set to False, then this drivers xml traffic will not be logged. Setting it to True, and the logging level to DEBUG, will log the drivers xml traffic. See the logging section of this documentation for further details.

The following attributes are not normally accessed, but are available if required, they are dealt with in more detail under SnoopEvents.

snoopall - Default False, gets set to True if this driver is snooping everything.

snoopdevices - Default empty set, becomes populated with remote devicenames if those devices are having all vectors snooped.

snoopvectors - Default empty dictionary. The keys will become tuples of remote (devicename,vectorname) of vectors that are being snooped. The values will be either None or lists of [timeout, timestamp].


There are two ways a driver can be run, assuming ‘driver’ is an instance of this class.

This coroutine outputs the xml data on stdout, and reads it on stdin:

await driver.asyncrun()

This could be awaited together with any other co-routines needed to run your instrument. Making your script executable, and using the above method should allow your driver to work with other parties INDI server software that expect stdin and stdout streams from drivers.

Alternatively, use indipyserver.IPyServer, this listens on the given host and port, to which a client can connect. Multiple drivers can be served, and multiple client connections can be made:

server = IPyServer(*drivers, host="localhost", port=7624, maxconnections=5)
await server.asyncrun()

You would use then use indipyterm, or other INDI client, to connect to this port, however note that if your client is running remotely, and you are connecting over a network, then in the above command “localhost” would need to be changed to the IP address of the servers listening port, or to “0.0.0.0” to listen on all ports.