Cynical Network

Cynical Network #

This plugin allows Indigo to make and receive network connections over the Internet. You may write text with actions. You may define events that trigger when a particular text pattern is seen in a received line, using regular expressions. You may enable and disable these events dynamically so as to shape a conversation - all without writing any Python or AppleScript code.

This plugin is primarily aimed at line-oriented text conversations. For handling (light) binary data, see below.

There is no support for UDP. (If you don’t know what that is, don’t worry about it.)

If you want a worked example, here is how to control a TiVO using this plugin.

Overview #

For each network connection you want to use, create either a TCP Out device if you want to originate it, or a TCP In device if you want to wait for an incoming connection. Choose your port numbers well. For TCP Out devices, you can either use an explicit Connect action to connect it, or you can check the Auto-connect configuration checkbox to ask Indigo to keep the connection up all the time. For TCP In devices, the connection will happen when some program out there asks for it.

Use Device State Change event triggers on these network devices to respond to the situation. Watch for connected to respond to the start of a connection, and for disconnected for the end - which may come because the other end has broken off the connection or the network has failed.

Use Send Text actions to send whole or partial text lines to a connected device. Use Recognized Input and Unrecognized Input triggers to respond to incoming text. Enable and disable individual triggers, using ordinary Indigo actions, to tailor the response to the state of your conversation. End connections with the Disconnect action. A device will automatically disconnect if you disable it.

Devices #

The TCP Out and TCP In devices support TCP (stream) connections to or from some Internet service somewhere (which may be on your own computer). They differ only in how a connection is established; once connected, they behave exactly the same:

  • The Send Text action sends text to the connection.
  • The Disconnect action terminates the connection. If the remote end drops the connection, it terminates automatically.
  • Use the Recognized Input and Unrecognized Input event triggers to respond to received lines of text.
  • If you have specified a Last line read variable in the device configuration, you can always find the last line received there, for use in AppleScript or Python actions.

TCP Out Device #

This device is a way to make an outgoing TCP connection to some server out there. By default, it remains idle until a Connect action for it is executed, at which point it attempts to make the connection.

If the Auto-connect checkbox is enabled, this is automatically done whenever the connection is idle. In other words, an autoconnect device will continuously attempt to establish and maintain its connection. Note that applying the Disconnect action to an autoconnecting device will terminate the present connection but immediately attempt to establish a new one. The only way to stop an auto-connecting device from trying to re-establish its connection is to disable it.

Configuration Settings #

Setting Value Description
Network Host Address IP Address Name or address of host to connect to. Leave this empty for the computer running the Indigo server (localhost).
Port number or name Port number or name to connect to.
Auto-connect bool Make the device persistent: it automatically tries to connect whenever it is idle.
Line Endings menu What characters constitute the end of a line. See Line Endings below.
Last line read variable name Automatically set a variable with this name to the last full line received by the connection. Leave blank to use no variable.

TCP In Device #

This device is Indigo’s way of listening for some other program out there making a connection to Indigo. When a request for a connection is received, Indigo establishes the connection and handles it as described above. Only one connection can ever be active for each such device; additional incoming requests are queued (within reason) and accepted when the present connection terminates.

Configuration Settings #

Setting Value Description
Port" number or name Port number or name to listen on. This port must not already be in use by the system.
Line Endings menu What characters constitute the end of a line. See Line Endings below.
Last line read variable name Automatically set a variable with this name to the last full line received by the connection. Leave blank to use no variable.

Device States (both TCP In and TCP Out) #

State Meaning
idle The device is ready but not connected. It is not doing anything.
connecting The device is trying to establish a connection.
connected The device is connected to a network peer, and you can send and receive text right now.
disconnected A connection has just ended. This state is momentary.
preparing The device is trying to initialize the network port.
unavailable Something has gone wrong and the device cannot be used right now. Check the Indigo log for details.

Use a device state change trigger for the connected state to catch the start of a new connection, regardless of how it got started. Use a trigger for the disconnected state to catch the end of a connection.

The disconnected state is momentary; after a brief hesitation, the state will automatically move on - usually to idle or connecting. This state exists just so you can have a device state change trigger that fires whenever a connection ends, regardless of the circumstances.

Actions #

Connect Action #

Tells a TCP Out device to try and establish its connection. This does nothing (and produces an error message in the Indigo log) if the connection is not idle.

A persistent connection automatically performs a connect whenever it is becoming idle.

Connect cannot be used on TCP In devices; they are connecting automatically in response to incoming connection requests as long as they are enabled.

Disconnect Action #

Immediately disconnects a connected TCP In or TCP Out device. The device state becomes momentarily disconnected. The device then follows its normal progression:

  • A TCP In device picks up the next waiting incoming connection. If there is none, it will wait for one to arrive.
  • An ordinary TCP Out connection will become idle and wait until a Connect action tells it to connect again.
  • A persistent TCP Out device will immediately attempt to re-establish the connection.

Disabling a device automatically disconnects it. If you want to finish an existing connection but then not allow subsequent ones, disable the device in a device state change trigger for the disconnected state.

Send Text Action #

Send some text to a connected device. A full line is sent by automatically appending the device’s line ending characters - see Line Endings. The partial checkbox defeats this completion and sends a partial line instead.

The text value may contain Python escape codes which allows you to include unprintable characters in the string. This will also allow you to send unusual line ending characters if needed. If needed, this allows you to send arbitrary binary data using \x escape codes. Be aware that backslash characters “" will need to be escaped by doubling them.

This action will fail (with an error message in the Indigo log) if the device state is not currently connected.

Configuration Settings #

Setting Value Description
Text string The text to send through the connection. May include escape sequences.
Partial bool If set, send a partial line by not appending the line ending characters.

Events #

Incoming text lines on a connected device trigger events you can use to take action. Recognized Input events can be configured with regular expressions to match particular input lines. They are only eligible for matching when they are enabled; you can conduct an entire network conversation by selectively enabling and disabling these event triggers based on the situation.

If you need to deal with arbitrary input, use Unrecognized Input events and read the actual line received from the Indigo variable configured for the device. This is also useful for handling unexpected error cases.

Normally, devices only processes event triggers when a full line (as defined by the device’s line ending setting) has been received. Partial line input is kept indefinitely, waiting for the line ending to arrive; but you can’t see or act upon it until that happens. If you must handle partial line input, you can use the None line ending, but you must be prepared for data to arrive in arbitrary chunks of characters, subject to the vagaries of network traffic.

Recognized Input Event #

This event applies to a particular TCP device. It specifies a regular expression pattern. Whenever that device receives a complete line that matches this regular expression, the event triggers and Indigo performs its actions. If you specified a variable in the device configuration, it will contain the (whole) line that triggered the event.

Configuration Settings #

Setting Value Description
Connection device The device to monitor for incoming text.
Pattern regular expression A $regex pattern to match.
Strict Match bool If set, the pattern must match the whole line (excluding the line ending). If unset, any part of the line can trigger a match.
Last line matched variable name Automatically set a variable with this name to the last text matched by this event. Leave blank to use no variable.

You can have any number of Recognized Input events for the same device, but they should all have distinct patterns that do not overlap. If a received text line matches multiple enabled pattern events, the system will pick a single one at random and trigger it; the others will not fire for that line.

The last line matched variable, if specified, will be set to the value of the first matching group (string within “()” characters) in the pattern. If the pattern contains no parentheses, it will be assigned the whole line (without the line ending).

Unrecognized Input Event #

This event fires whenever a given network device receives a complete text line that does not match any enabled Recognized Input triggers for that device. It acts as a catch-all to consume unexpected input. If you have no enabled Recognized Input triggers, each incoming line will be delivered this way. If you have multiple enabled triggers of this type for a device, they will all fire in some unspecified order. If you specified a variable in the device configuration, it will contain the (entire) line that triggered the event.

Configuration Settings #

Setting Value Description
Connection device The device to monitor for incoming text.

Notes #

Line Endings #

Devices handle data one line at a time. The line ending configuration setting determines how lines are recognized. You can set this separately for each connection device.

Setting Byte(s) Notes
Newline \x0A Common in UNIX systems and their protocols.
CRNL \x0D\x0A Used in “classic” Internet protocols and on Windows.
Carriage Return \x0D Used in some embedded systems.
Null \x00 Lines are separated by zero (null) bytes.
None nothing Use no lines at all.

The none line ending means that we’re not really processing lines at all. The Send Text action does not append anything to outgoing text; and anything arriving from the remote partner is immediately treated as a “line” and delivered accordingly, even if it is just a single character at a time. Use this if you have to work with a protocol that is not line oriented. Happily, this is rare in practice.

Note that devices will add the designated line ending bytes automatically to outgoing text, unless you use the Partial option on the Send Text action. You can put suitable ending characters into your text explicitly using Python character escape codes; just make sure you don’t do both or you’ll end up inadvertently sending an extra (empty) line.

Binary Data #

Handling binary data in an otherwise line-oriented protocol is not hard. If the binary data is constant, use Python escape sequence codes in your output text. Incoming lines treat binary data as ordinary characters, and you can match them likewise.

If you need to send variable binary data, take a look at the Python struct module. Combined with a plugin formula for the Send Text action, this allows you to pack arbitrary values into bytes.

If the protocol does not use recognizable line endings (it is completely binary), then choose the None line ending to avoid sprinkling unexpected characters into your output. This also disables scanning for full lines on input, and any bytes arriving from the peer are delivered immediately, using the standard event mechanism, as a “line”. It is up to you to assemble these bytes into whatever groups make sense in your case. Beware that the network sometimes splits seemingly contiguous bytes into multiple chunks, so assuming that an entire data packet will arrive intact is unwise. You must use whatever termination bytes or length fields are defined for your protocol.

If this all turns out to be too hard for you, it may be time to write your own plugin. Happily, this turns out to be fairly rare.

Release Notes #

Notable releases:

Release Notes
1.8.0 Released under the Apache 2.0 license.
1.7.2 Support for Indigo 7.
1.5.0 Better support for Indigo 6.
1.1.0 Add variable assignment to Recognized Input events. Support field formulas.
1.0.2 Send Text actions allow Python string escape sequences.
1.0.1 Explicit line ending specifications now work as expected.
1.0.0 Initial release.