Acting on the device

Example module: helloindiactions.

In Creating properties dynamically, the action button modified the module’s internal state (hellodyn). Here, the action actually modifies something on the device — the device itself persists it, not the module.

HelloIndiActions::HelloIndiActions(...)
    : IndiModule(...)
{
    giveMeADevice("device", "Device", INDI::BaseDevice::GENERAL_INTERFACE);
    giveMeAnActions();

    OST::ElementBool *saveconfig = new OST::ElementBool("saveconfig", "Save device config", "1", "");
    saveconfig->setDirectEdit(true);
    getProperty("actions")->addElt(saveconfig);
}

giveMeAnActions() builds the actions property (empty at first); addElt() adds the saveconfig element to it, in C++, as in Creating properties dynamically — this chapter changes nothing about how a property is built, only what happens when it is triggered.

void HelloIndiActions::onExternalEvent(OST::ExtEvent event)
{
    if (event.ev != OST::ExtEvType::SV || event.prpkey != "actions" || event.eltkey != "saveconfig") return;

    sendModNewSwitch(getString("devices", "device"), "CONFIG_PROCESS", "CONFIG_SAVE", ISS_ON);
    getEltBool("actions", "saveconfig")->setValue(false, true);
}

CONFIG_PROCESS/CONFIG_SAVE is a standard INDI switch, present on every driver (save the current configuration) — which makes this example testable against any connected device, without depending on a feature specific to one particular simulator.

sendModNewSwitch/Number/String are tricky — not always the right tool

These helpers only write one element at a time: they fetch the property as currently known on the client side, change the value of the targeted element, then send back the whole vector — the other elements go out with their last known value, not necessarily 0, not necessarily what the driver expects.

Concrete example: sending a guide pulse to a mount. TELESCOPE_TIMED_GUIDE_NS has two elements, TIMED_GUIDE_N and TIMED_GUIDE_S — you only ever pulse in one direction at a time, so the driver expects to receive both values on every send, one of them explicitly set to 0. By only writing TIMED_GUIDE_N via sendModNewNumber(), TIMED_GUIDE_S goes out with its last known value — potentially nonzero, which the driver may simply ignore or misinterpret.

In this kind of case, it’s better to drop straight down to the INDI::BaseClient level (see the overview: an IndiModule is a real INDI client): build the full INDI::PropertyNumber yourself, explicitly set every element, then call sendNewNumber() directly — rather than trusting a helper designed for the simple case of a property with a single relevant element.