With INDI, just remember, you have to set all values in the property for it to properly configure, so you would have to set every value in CCD_CONTROLS, even if all of the values are not changing. The number of values in the property can actually change between INDI/driver releases and will be in different orders between vendors.
I borrowed some very nice methods that Marco Gulino he developed for indi-lite-tools where if you want to change a single value, it queries all of the existing values and sets each value to the existing value while only changing the properties you want to change. They also allow you set properties by name, instead of just by index.
You can find what I have in indi-allsky here: https://github.com/aaronwmorris/indi-allsky/blob/main/indi_allsky/camera/indi.py
Here is some rough code
import PyIndi
import time
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
def main(self):
ccd_device = indiclient.getDevice("CCD NAME")
indi_config = {
"PROPERTIES" : {
"CCD_CONTROLS" : {
"Gain" : 100,
"Offset" : 10
}
},
"SWITCHES" : {
"CCD_VIDEO_FORMAT" : {
"on" : ["ASI_IMG_RAW16"], # example of setting a switch
"off" : ["ASI_IMG_RAW8"] # off settings are optional
}
}
}
configureDevice(ccd_device, indi_config)
def configureDevice(self, device, indi_config):
if isinstance(device, FakeIndiCcd):
# ignore configuration
return
### Configure Device Switches
for k, v in indi_config.get('SWITCHES', {}).items():
logger.info('Setting switch %s', k)
self.set_switch(device, k, on_switches=v.get('on', []), off_switches=v.get('off', []))
### Configure Device Properties
for k, v in indi_config.get('PROPERTIES', {}).items():
logger.info('Setting property (number) %s', k)
self.set_number(device, k, v)
### Configure Device Text
for k, v in indi_config.get('TEXT', {}).items():
logger.info('Setting property (text) %s', k)
self.set_text(device, k, v)
def set_number(self, device, name, values, sync=True, timeout=None):
#logger.info('Name: %s, values: %s', name, str(values))
c = self.get_control(device, name, 'number')
if c.getPermission() == PyIndi.IP_RO:
logger.error('Number control %s is read only', name)
return c
for control_name, index in self.__map_indexes(c, values.keys()).items():
logger.info('Setting %s = %s', c[index].getLabel(), str(values[control_name]))
c[index].setValue(values[control_name])
self.sendNewNumber(c)
if sync:
self.__wait_for_ctl_statuses(c, timeout=timeout)
return c
def set_switch(self, device, name, on_switches=[], off_switches=[], sync=True, timeout=None):
c = self.get_control(device, name, 'switch')
if c.getPermission() == PyIndi.IP_RO:
logger.error('Switch control %s is read only', name)
return c
is_exclusive = c.getRule() == PyIndi.ISR_ATMOST1 or c.getRule() == PyIndi.ISR_1OFMANY
if is_exclusive :
on_switches = on_switches[0:1]
off_switches = [s.getName() for s in c if s.getName() not in on_switches]
for index in range(0, len(c)):
current_state = c[index].getState()
new_state = current_state
if c[index].getName() in on_switches:
logger.info('Enabling %s (%s)', c[index].getLabel(), c[index].getName())
new_state = PyIndi.ISS_ON
elif is_exclusive or c[index].getName() in off_switches:
new_state = PyIndi.ISS_OFF
c[index].setState(new_state)
self.sendNewSwitch(c)
return c
def set_text(self, device, name, values, sync=True, timeout=None):
c = self.get_control(device, name, 'text')
if c.getPermission() == PyIndi.IP_RO:
logger.error('Text control %s is read only', name)
return c
for control_name, index in self.__map_indexes(c, values.keys()).items():
logger.info('Setting %s = %s', c[index].getLabel(), str(values[control_name]))
c[index].setText(values[control_name])
self.sendNewText(c)
if sync:
self.__wait_for_ctl_statuses(c, timeout=timeout)
return c
def __map_indexes(self, ctl, values):
result = {}
for i, c in enumerate(ctl):
#logger.info('Value name: %s', c.getName()) # useful to find value names
if c.getName() in values:
result[c.getName()] = i
return result
def __wait_for_ctl_statuses(self, ctl, statuses=[PyIndi.IPS_OK, PyIndi.IPS_IDLE], timeout=None):
started = time.time()
if timeout is None:
timeout = 5
while ctl.getState() not in statuses:
if ctl.getState() == PyIndi.IPS_ALERT and 0.5 > time.time() - started:
raise RuntimeError('Error while changing property {0}'.format(ctl.getName()))
elapsed = time.time() - started
if 0 < timeout < elapsed:
raise Exception('Timeout error while changing property {0}'.format(ctl.getName()))
time.sleep(0.15)