{"id":28416321,"url":"https://github.com/deep-symmetry/ola-clojure","last_synced_at":"2025-12-12T01:36:14.902Z","repository":{"id":36206813,"uuid":"40511068","full_name":"Deep-Symmetry/ola-clojure","owner":"Deep-Symmetry","description":"A Clojure library for communicating with the Open Lighting Architecture","archived":false,"fork":false,"pushed_at":"2023-03-12T17:52:16.000Z","size":704,"stargazers_count":8,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-04T03:59:35.022Z","etag":null,"topics":["clojure","ola","ola-client"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Deep-Symmetry.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-08-10T23:30:19.000Z","updated_at":"2024-11-06T21:12:44.000Z","dependencies_parsed_at":"2022-08-31T05:03:37.649Z","dependency_job_id":null,"html_url":"https://github.com/Deep-Symmetry/ola-clojure","commit_stats":null,"previous_names":["brunchboy/ola-clojure"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/Deep-Symmetry/ola-clojure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Deep-Symmetry%2Fola-clojure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Deep-Symmetry%2Fola-clojure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Deep-Symmetry%2Fola-clojure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Deep-Symmetry%2Fola-clojure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Deep-Symmetry","download_url":"https://codeload.github.com/Deep-Symmetry/ola-clojure/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Deep-Symmetry%2Fola-clojure/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262084649,"owners_count":23256279,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","ola","ola-client"],"created_at":"2025-06-03T20:08:18.597Z","updated_at":"2025-12-12T01:36:14.816Z","avatar_url":"https://github.com/Deep-Symmetry.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ola-clojure\n\n[![project chat](https://img.shields.io/badge/chat-on%20zulip-brightgreen)](https://deep-symmetry.zulipchat.com/#narrow/stream/318697-afterglow) \u003cimage align=\"right\" width=\"275\"\nsrc=\"doc/assets/ola-clojure-padded-left.png\"\u003e\u003cbr/\u003e\u003cbr/\u003e\nA\n[Clojure](http://clojure.org) library for communicating with the\n[Open Lighting Architecture](https://www.openlighting.org/ola/).\n\n[Protocol Buffers](https://developers.google.com/protocol-buffers/docs/overview)\nare used to efficiently communicate with the `olad` daemon via its\n[RPC Service](https://docs.openlighting.org/doc/latest/rpc_system.html).\n\nThis project was extracted from\n[Afterglow](https://github.com/brunchboy/afterglow#afterglow), so that\nother Clojure projects could communicate with OLA without having to\npull in all of Afterglow.\n\n[![License](https://img.shields.io/github/license/brunchboy/ola-clojure.svg)](#license)\n\n## Overview\n\nola-clojure uses the\n[OLA RPC system](http://docs.openlighting.org/ola/doc/latest/rpc_system.html#sec_RPCHeader)\nto communicate with the Open Lighting Architecture. Its build process\nscans the Protobuf\n[specification file](https://github.com/brunchboy/ola-clojure/blob/master/resources/proto/Ola.proto)\nthat defines the OLA methods exported by `olad`, and uses a code\ngenerator to create Clojure functions which marshal maps you pass them\ninto properly formatted Protocol Buffers, open the `olad` connection\nif necessary, wrap an RPC message telling `olad` what you want to do,\nand send it. When a response is received (or if the send attempt\nfails), a callback function that you supply will be invoked with\neither the response or a failure indication.\n\nThis means that in the most common configuration, of talking to `olad`\non the default port `9010` on the local machine, you need to write\nalmost no code. But you can also configure ola-clojure to talk to\nother ports and other machines, as described\n[below](#connection-configuration).\n\n## Getting Help\n\n\u003ca href=\"http://zulip.com\"\u003e\u003cimg align=\"right\" alt=\"Zulip logo\"\n src=\"doc/assets/zulip-icon-circle.svg\"\n width=\"128\" height=\"128\"\u003e\u003c/a\u003e\n\nDeep Symmetry\u0026rsquo;s projects are generously sponsored with hosting by \u003ca\nhref=\"https://zulip.com\"\u003eZulip\u003c/a\u003e, an open-source modern team chat\napp designed to keep both live and asynchronous conversations\norganized. Thanks to them, you can \u003ca\nhref=\"https://deep-symmetry.zulipchat.com/#narrow/stream/318697-afterglow\"\u003echat\nwith our community\u003c/a\u003e, ask questions, get inspiration, and share your\nown ideas.\n\n## Usage\n\n1. If you haven't already,\n   [Install OLA](https://www.openlighting.org/ola/getting-started/downloads/).\n   (On the Mac I recommend using [Homebrew](http://brew.sh) which lets\n   you simply `brew install ola`). Once you launch the `olad` server\n   you can interact with its embedded\n   [web server](http://localhost:9090/ola.html), which is very helpful\n   in seeing whether anything is working; you can even watch live DMX\n   values changing.\n\n2. Set up a Clojure project using [Leiningen](http://leiningen.org) or [Boot](https://github.com/boot-clj/boot#boot--).\n\n3. Add this project as a dependency:\n   [![Clojars Project](https://img.shields.io/clojars/v/ola-clojure.svg)](https://clojars.org/ola-clojure)\n\n4. In the namespace from which you want to communinicate with `olad`,\n   add this to the `:require` section of the `ns` form:\n\n   `[ola-clojure.ola-service :as ola]`\n\nAnd you are ready to invoke methods in `olad`. The methods which are\nexported by `ola-service` are parsed from the `OlaServerService`\nsection of\n[Ola.proto](https://github.com/brunchboy/ola-clojure/blob/master/resources/proto/Ola.proto#L374-L402).\nThe messages which the methods use as parameters are defined earlier in\nthe file. Consider for example the `GetUniverseInfo`\n[method](https://github.com/brunchboy/ola-clojure/blob/master/resources/proto/Ola.proto#L385):\n\n```protobuf\n// RPCs handled by the OLA Server\nservice OlaServerService {\n  // ...\n  rpc GetUniverseInfo (OptionalUniverseRequest) returns (UniverseInfoReply);\n  // ...\n}\n```\n\nThe request message, `OptionalUniverseRequest` is specified\n[earlier](https://github.com/brunchboy/ola-clojure/blob/master/resources/proto/Ola.proto#L257-L260)\nin the file:\n\n```protobuf\n// request info about a universe\nmessage OptionalUniverseRequest {\n  optional int32 universe = 1;\n}\n```\n\nArmed with this information, and having properly required and aliased\n`ola-service` (if you are just experimenting interactively at the\nREPL, `(require '[ola-clojure.ola-service :as ola])` at this point),\nwe can obtain a list of universes from `olad` like so:\n\n```clojure\n(ola/GetUniverseInfo #(clojure.pprint/pprint %))\n```\n\nThe only required argument to the wrapper functions is a callback\nfunction that will be invoked with the results. In this case, we are\npassing an anonymous function which simply uses the standard Clojure\npretty-printer to format the map we get back. You may have to hit\n\u003ckbd\u003eEnter\u003c/kbd\u003e again to see the output, because connecting to `olad`\nand sending the RPC happens asynchronously, and the call to\n`GetUniverseInfo` returns immediately, as that process is just\nbeginning. But once you do, you will see something like this:\n\n```clojure\n{:response\n {:universe\n  [{:universe 0,\n    :name \"Dummy Universe\",\n    :merge_mode :LTP,\n    :input_port_count 0,\n    :output_port_count 1,\n    :rdm_devices 6,\n    :output_ports\n    [{:port_id 0,\n      :priority_capability 0,\n      :description \"Dummy Port\",\n      :universe 0,\n      :active true,\n      :supports_rdm true}]}\n   {:universe 1,\n    :name \"DMX USB Out 1\",\n    :merge_mode :LTP,\n    :input_port_count 0,\n    :output_port_count 1,\n    :rdm_devices 0,\n    :output_ports\n    [{:port_id 0,\n      :priority_capability 0,\n      :description \"Serial #: 02615032, firmware 4.8\",\n      :universe 1,\n      :active true,\n      :supports_rdm true}]}\n   {:universe 2,\n    :name \"DMX USB Out 2\",\n    :merge_mode :LTP,\n    :input_port_count 0,\n    :output_port_count 1,\n    :rdm_devices 0,\n    :output_ports\n    [{:port_id 1,\n      :priority_capability 0,\n      :description \"Serial #: 02615032, firmware 4.8\",\n      :universe 2,\n      :active true,\n      :supports_rdm true}]}\n   {:universe 3,\n    :name \"DMX USB In 1\",\n    :merge_mode :LTP,\n    :input_port_count 1,\n    :output_port_count 0,\n    :rdm_devices 0,\n    :input_ports\n    [{:port_id 0,\n      :priority_capability 1,\n      :description \"Serial #: 02615032, firmware 4.8\",\n      :universe 3,\n      :active true,\n      :priority_mode 1,\n      :priority 100,\n      :supports_rdm false}]}]}}\n```\n\n\u003e If you are sending a \"fire and forget\" message and you don't care\n\u003e about the result, you can pass `nil` for your callback function.\n\nThis is exactly what we would expect from looking at the\n[specifications](https://github.com/brunchboy/ola-clojure/blob/master/resources/proto/Ola.proto#L257-L275)\nof the reply message:\n\n```protobuf\nmessage UniverseInfo {\n  required int32 universe = 1;\n  required string name = 2;\n  required MergeMode merge_mode = 3;\n  required int32 input_port_count = 4;\n  required int32 output_port_count = 5;\n  required int32 rdm_devices = 6;\n  repeated PortInfo input_ports = 7;\n  repeated PortInfo output_ports = 8;\n}\n\nmessage UniverseInfoReply {\n  repeated UniverseInfo universe = 1;\n}\n```\n\nIn the response, the Protocol Buffer structures have all been expanded\ninto ordinary Clojure data structures for convenient access.\n\nAs we saw in its specification above, the `GetUniverseInfo` takes an\noptional parameter identifying a specific universe of interest. To\npass parameters using `ola-clojure`, you simply supply a normal\nClojure map with keys and values corresponding to the message\ndefinitions within the Protobuf specification. In this case, the\nparameter is named `universe` and takes an integer, so the following\nvariant gets information about a single universe:\n\n```clojure\n(ola/GetUniverseInfo {:universe 1} #(clojure.pprint/pprint %))\n```\nAs expected:\n\n```clojure\n{:response\n {:universe\n  [{:universe 1,\n    :name \"DMX USB Out 1\",\n    :merge_mode :LTP,\n    :input_port_count 0,\n    :output_port_count 1,\n    :rdm_devices 0,\n    :output_ports\n    [{:port_id 0,\n      :priority_capability 0,\n      :description \"Serial #: 02615032, firmware 4.8\",\n      :universe 1,\n      :active true,\n      :supports_rdm true}]}]}}\n```\n\nWhat happens if we ask for a universe that doesn't exist?\n\n```clojure\n(ola/GetUniverseInfo {:universe 42} #(clojure.pprint/pprint %))\n```\n\nThis call initially returns `true` just like the others, but yields different output:\n\n```clojure\n{:failed \"OLA RpcMessage failed: Universe doesn't exist\"}\n```\n\nNotice that instead of a `:response` key, there is a `:failed` key\ncontaining a description of the problem. This is how you can tell that\nsomething went wrong. If there is an exception that provides more\ncontext to the problem, it will be present under the key `:thrown`.\n\nIn addition to the result map printed by our callback function, you\nwill see a different line of output:\n\n```\n15-Aug-12 10:34:02 Alacrity.local WARN [ola-clojure.ola-client] -\n                   OLA RpcMessage failed: Universe doesn't exist\n```\n\nThis comes from the logging mechanism used by ola-clojure. See\n[below](#logging-configuration) for information about how that can be\nconfigured.\n\nOther methods work in the same way, like\n[GetPlugins](https://github.com/brunchboy/ola-clojure/blob/master/resources/proto/Ola.proto#L376):\n\n```clojure\n(ola/GetPlugins #(clojure.pprint/pprint %))\n```\n\n```clojure\n{:response\n {:plugin\n  [{:plugin_id 1, :name \"Dummy\", :active true, :enabled true}\n   {:plugin_id 2, :name \"ArtNet\", :active true, :enabled true}\n   {:plugin_id 3, :name \"ShowNet\", :active true, :enabled true}\n   {:plugin_id 4, :name \"ESP Net\", :active true, :enabled true}\n   {:plugin_id 5, :name \"Serial USB\", :active true, :enabled true}\n   {:plugin_id 6, :name \"Enttec Open DMX\", :active true, :enabled true}\n   {:plugin_id 7, :name \"SandNet\", :active false, :enabled false}\n   {:plugin_id 8, :name \"StageProfi\", :active false, :enabled false}\n   {:plugin_id 9, :name \"Pathport\", :active true, :enabled true}\n   {:plugin_id 11, :name \"E1.31 (sACN)\", :active true, :enabled true}\n   {:plugin_id 12, :name \"USB\", :active true, :enabled true}\n   {:plugin_id 14, :name \"OSC\", :active true, :enabled true}\n   {:plugin_id 16, :name \"KiNET\", :active true, :enabled true}\n   {:plugin_id 17, :name \"KarateLight\", :active false, :enabled false}\n   {:plugin_id 18,\n    :name \"Milford Instruments\",\n    :active false,\n    :enabled false}\n   {:plugin_id 19, :name \"Renard\", :active true, :enabled true}\n   {:plugin_id 21,\n    :name \"Open Pixel Control\",\n    :active true,\n    :enabled true}\n   {:plugin_id 22, :name \"GPIO\", :active true, :enabled true}]}}\n```\n\nOr, extracting and just printing the most important part of the\nresponse to\n[GetPluginDescription](https://github.com/brunchboy/ola-clojure/blob/master/resources/proto/Ola.proto#L378-L379):\n\n```clojure\n(ola/GetPluginDescription {:plugin_id 5}\n                          #(print (get-in % [:response :description])))\n```\n\n```\nSerial USB Plugin\n----------------------------\n\nThis plugin supports DMX USB devices that emulate a serial port. This\nincludes:\n - Arduino RGB Mixer\n - DMX-TRI \u0026 RDM-TRI\n - DMXking USB DMX512-A, Ultra DMX, Ultra DMX Pro\n - DMXter4 \u0026 mini DMXter\n - Enttec DMX USB Pro\n - Robe Universe Interface\n\nSee http://opendmx.net/index.php/USB_Protocol_Extensions for more info.\n\n--- Config file : ola-usbserial.conf ---\n\ndevice_dir = /dev\nThe directory to look for devices in.\n\ndevice_prefix = ttyUSB\nThe prefix of filenames to consider as devices. Multiple keys are allowed.\n\nignore_device = /dev/ttyUSB\nIgnore the device matching this string. Multiple keys are allowed.\n\npro_fps_limit = 190\nThe max frames per second to send to a Usb Pro or DMXKing device.\n\ntri_use_raw_rdm = [true|false]\nBypass RDM handling in the {DMX,RDM}-TRI widgets.\n\nultra_fps_limit = 40\nThe max frames per second to send to a Ultra DMX Pro device.\n\nuucp_lock_path = /var/lock\nPath to check for UUCP Lock files.\n```\n\nFinally, as an example of a more interesting RPC you will likely want\nto call, here is the section of Afterglow's `show` namespace which\nsends an updated set of DMX control values to one of the show's\nuniverses:\n\n```clojure\n(let [levels (get buffers universe)]\n  (ola/UpdateDmxData {:universe universe :data (ByteString/copyFrom levels)}\n                     response-handler))\n```\n\nIn this example, `universe` contains the ID of a universe that the\nshow is controlling, and `levels` is a Java `byte` array containing\nthe desired DMX channel values for the universe. This invocation\ncauses `olad` to send those values to whatever plugin and interface is\ncontrolling that universe.\n\nThese examples, combined with the\n[Ola.proto](https://github.com/brunchboy/ola-clojure/blob/master/resources/proto/Ola.proto#L374-L402)\nspecification which creates the wrapper functions, will hopefully\nenable you to figure out how to send whatever messages you need to\n`olad`.\n\n### Connection Configuration\n\nIf you need to talk to `olad` on a different port or address, you can\ndo so by configuring the\n[ola-client](https://github.com/brunchboy/ola-clojure/blob/master/src/ola_clojure/ola_client.clj)\nnamespace which manages the connection on behalf of the RPC wrapper\nfunctions, before the connection is established:\n\n```clojure\n(require '[ola-clojure.ola-client :as ola-client])\n(reset! ola-client/olad-host \"172.30.246.32\")\n(reset! ola-client/olad-port 9200)\n```\n\nIf you do need to talk to an OLA server on a different machine, by\nchanging the `olad-host` value, this means that the communication will\nbe much slower than talking to a local process. Because of that, you\nwill almost certainly want to tell ola-client to change from using the\nunbuffered channel that it normally uses to gather messages you want\nto send to the OLA server. This default channel will block if you ever\ntry to send a second message while the first one is still being\nwritten to the network. To use a channel with a buffer, you call\n`use-buffered-channel`:\n\n```clojure\n(ola-client/use-buffered-channel)\n```\n\nAgain, this needs to be done before communication with the OLA server\nhas begun. The default buffer size holds 32 messages, and if it fills\nup because the network is unable to keep up with the rate at which you\nare trying to send messages, older messages will be discarded. This is\nprobably plenty large a buffer, and will not cause message loss under\nnormal circumstances, but you can specify a different buffer size by\ngiving an argument to `use-buffered-channel`:\n\n```clojure\n(ola-client/use-buffered-channel 48)\n```\n\nThe `ola-client` namespace also provides a `shutdown` function which\nyou can call if you ever want to explicitly close the `olad` connection:\n\n```clojure\n(ola-client/shutdown)\n```\n\nYou can use this if you have changed your connection parameters after\na connection was already established, and you want your new values to\ntake effect.\n\nThere is also a `start` function to open the connection again, but\nthere is no real need to call this, as the RPC wrapper functions will\nall call it if necessary.\n\n### Logging Configuration\n\nLike Afterglow, ola-clojure uses the excellent\n[Timbre](https://github.com/ptaoussanis/timbre) logging framework. If\nyou do nothing, log messages above the `debug` level will be written\nto the standard output. But you can configure it however you would\nlike, as described in its\n[documentation](https://github.com/ptaoussanis/timbre#configuration).\n\n## Status\n\nThis has been used without any changes since the beginning of the\nAfterglow project, although it was only recently separated into its\nown project, and cleaned up slightly in the process. More recently,\nsupport for buffered channels was added, to help people who want to\ntalk to OLA from Windows, where it can't run as a local process.\n\n## Bugs\n\nAlthough there are none known as of the time of this release, please\nlog [issues](https://github.com/brunchboy/ola-clojure/issues) as you\nencounter them!\n\n### References\n\n* Clojure implementation of Protocol Buffers via\n  [lein-protobuf](https://github.com/flatland/lein-protobuf) and\n  [clojure-protobuf](https://github.com/flatland/clojure-protobuf).\n* The\n  [Java OLA client](https://github.com/OpenLightingProject/ola/tree/master/java).\n\n## License\n\n\u003ca href=\"http://deepsymmetry.org\"\u003e\u003cimg align=\"right\" alt=\"Deep Symmetry\"\n src=\"doc/assets/DS-logo-github.png\" width=\"250\" height=\"150\"\u003e\u003c/a\u003e\n\nCopyright © 2015 [Deep Symmetry, LLC](http://deepsymmetry.org)\n\nDistributed under the\n[Eclipse Public License 1.0](http://opensource.org/licenses/eclipse-1.0.php),\nthe same as Clojure. By using this software in any fashion, you are\nagreeing to be bound by the terms of this license. You must not remove\nthis notice, or any other, from this software. A copy of the license\ncan be found in\n[doc/epl-v10.html](https://cdn.rawgit.com/brunchboy/ola-clojure/master/doc/epl-v10.html)\nwithin this project.\n\nThe OLA RPC Protobuf specification files are distributed under the GNU\nLesser General Public License,\n[version 2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeep-symmetry%2Fola-clojure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeep-symmetry%2Fola-clojure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeep-symmetry%2Fola-clojure/lists"}