Jekyll2022-01-17T00:00:23+00:00https://ehseejay.com/feed.xmlAdam’s ScribblesWriting, code, and photos by Adam JensenThe robots are coming for your maintainership2019-10-13T00:00:00+00:002019-10-13T00:00:00+00:00https://ehseejay.com/2019/10/13/the-robots-are-coming-for-your-maintainership<p>I recently got access to GitHub Actions through the beta program, and it got me thinking about what parts of my life as both a hobbyist and professional programmer could be automated. Adding continuous integration to a handful of side-project repositories was an easy one. For something juicier, I wondered if it would be possible to automate the often tedious process of updating packages for Linux distributions. I’m the maintainer of a couple of packages for Alpine Linux, and it would be nice to let the machines do the boring parts.</p>
<p>After thinking for a bit, it seemed possible. There were several problems to solve:</p>
<ul>
<li>Monitor for new releases. I want my packages to stay current, and that means I have to remember to check the upstream repositories for updates. That’s fragile and annoying.</li>
<li>If there’s a new release, fetch and build the commit for it</li>
<li>If the build succeeds, create a branch that bumps the package’s version number (and therefore the source commit) to match the new release</li>
<li>Notify me if there’s a new version or a problem with the build</li>
</ul>
<h2 id="lets-build-it">Let’s build it</h2>
<p>For the impatient, the final workflow is <a href="https://github.com/acj/aports/blob/a1de66fcb0c90e74d3b5953577be3f6a575bcd3e/.github/workflows/bcc.yaml">here</a>. I’ll go through each sub-problem and highlight the part of the workflow that solves it.</p>
<p>If you’re not familiar with GitHub Actions yet, I recommend taking a quick look at the <a href="https://help.github.com/en/articles/about-github-actions#core-concepts-for-github-actions">core concepts</a> documentation so that words like “workflow”, “step”, and “action” will have the right meaning.</p>
<h3 id="monitor-for-new-releases">Monitor for new releases</h3>
<p>My hope was that Actions could run code in my repository in response to an event in a <em>different</em> repository. For example, whenever a new <code class="language-plaintext highlighter-rouge">release</code> event is triggered on the <a href="https://github.com/iovisor/bcc">iovisor/bcc</a> repository, I’d like an action to run in one of my personal repositories, such as a fork of the upstream one. Unfortunately, that doesn’t seem possible – at least for now – without having an external mechanism to trigger the action. (I’d love to be wrong about this.)</p>
<p>The backup plan was to use Actions’ support for cron jobs. That is, you can <a href="https://help.github.com/en/articles/workflow-syntax-for-github-actions#onschedule">schedule</a> an action to run periodically without any other input or trigger. Using a schedule instead of an event trigger introduces other challenges, but it’s reliable and simple to set up, and it’s the solution that I decided to use.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">on</span><span class="pi">:</span>
<span class="na">push</span><span class="pi">:</span>
<span class="na">branches</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">actions/abuild/*</span>
<span class="na">schedule</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s1">'</span><span class="s">0</span><span class="nv"> </span><span class="s">9</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*'</span>
</code></pre></div></div>
<p>In addition to running the action every day at 9am UTC, I configured the action to run every time I push commits to any branch whose name matches <code class="language-plaintext highlighter-rouge">actions/abuild/*</code>. This makes testing and experimenting <em>much</em> easier. It’s important to note, however, that cron jobs only run for the master branch.</p>
<p>Among the challenges of using a scheduled action is the fact that, on most days, there will be nothing to do. The workflow will need to recognize that there hasn’t been a new release and terminate the build to conserve CPU time – but without marking the build as failed. Actions provides an <a href="https://help.github.com/en/articles/workflow-syntax-for-github-actions#jobsjob_idstepsif">if</a> derivative that’s very helpful here.</p>
<p>A related challenge surfaces on the days <em>after</em> a new release has shipped. It can take days or even weeks to get a new package version reviewed and merged upstream, and in the meantime the scheduled action continues to run each morning. To avoid generating errors or failed builds during that time period, the workflow has to recognize that a branch already exists for the new version. Solving this problem wasn’t too painful and led to a second action that I’ll discuss later.</p>
<h3 id="fetch-and-build-the-commit-for-a-new-release">Fetch and build the commit for a new release</h3>
<p>To actually relieve the maintenance burden, my workflow needs to fetch the new version of bcc and build it as closely as possible to the way that the Alpine project’s continuous integration system works. It would be a bummer to do all of this work only to have the new branch fail in Alpine’s CI. Luckily, the Alpine developer tooling is pretty good and includes a tool called <a href="https://github.com/alpinelinux/abuild">abuild</a> that can do most of the heavy lifting. The first piece of this puzzle is <a href="https://github.com/acj/action-abuild">action-abuild</a>, an action that wraps the <code class="language-plaintext highlighter-rouge">abuild</code> tool and takes care of details like package signing.</p>
<p>If anything goes wrong during this stage, there’s usually an issue with the Alpine-specific patches that are applied before bcc is compiled, and I’ll need to manually fix them. Automation can’t solve that problem just yet.</p>
<p>This stage comprises three related steps: figure out which version we want, check out the repository, and then try to build the new version. Note the use of <code class="language-plaintext highlighter-rouge">echo ::set-output ...</code>, which is a <a href="https://help.github.com/en/articles/development-tools-for-github-actions#set-an-output-parameter-set-output">clever mechanism</a> for returning outputs from bash- and Dockerfile-based actions.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Resolve package and release versions</span>
<span class="na">id</span><span class="pi">:</span> <span class="s">resolve_versions</span>
<span class="na">env</span><span class="pi">:</span>
<span class="na">GITHUB_REPO</span><span class="pi">:</span> <span class="s">iovisor/bcc</span>
<span class="na">PACKAGE_PATH</span><span class="pi">:</span> <span class="s">community/bcc</span>
<span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
<span class="s">PACKAGE_VERSION=$(curl -s https://raw.githubusercontent.com/alpinelinux/aports/master/$PACKAGE_PATH/APKBUILD | grep "pkgver=" | sed -E 's/pkgver=//g')</span>
<span class="s">RELEASE_VERSION=$(curl -s https://github.com/$GITHUB_REPO/releases.atom | grep "<title>" | grep -G -o "v[^ <]\+" | head -1 | tr -d 'v')</span>
<span class="s">if [ "$RELEASE_VERSION" == "$PACKAGE_VERSION" ]; then</span>
<span class="s">echo ::set-output name=have_new_version::false</span>
<span class="s">else</span>
<span class="s">echo ::set-output name=have_new_version::true</span>
<span class="s">echo ::set-output name=package_path::"$PACKAGE_PATH"</span>
<span class="s">echo ::set-output name=package_version::"$PACKAGE_VERSION"</span>
<span class="s">echo ::set-output name=release_version::"$RELEASE_VERSION"</span>
<span class="s">echo ::set-output name=branch_name::"$PACKAGE_PATH-to-$RELEASE_VERSION"</span>
<span class="s">echo ::set-output name=commit_message::"$PACKAGE_PATH: update to $RELEASE_VERSION"</span>
<span class="s">fi</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Check out aports</span>
<span class="na">if</span><span class="pi">:</span> <span class="s">steps.resolve_versions.outputs.have_new_version == 'true'</span>
<span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@master</span>
<span class="na">with</span><span class="pi">:</span>
<span class="na">fetch-depth</span><span class="pi">:</span> <span class="m">1</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Try building the new release version</span>
<span class="na">if</span><span class="pi">:</span> <span class="s">steps.resolve_versions.outputs.have_new_version == 'true'</span>
<span class="na">uses</span><span class="pi">:</span> <span class="s">acj/action-abuild@v1.0.0</span>
<span class="na">with</span><span class="pi">:</span>
<span class="na">PACKAGE_PATH</span><span class="pi">:</span> <span class="s">$</span>
<span class="na">PACKAGE_VERSION</span><span class="pi">:</span> <span class="s">$</span>
<span class="na">RELEASE_VERSION</span><span class="pi">:</span> <span class="s">$</span>
</code></pre></div></div>
<h3 id="if-the-build-succeeds-create-a-branch-with-an-updated-version-number">If the build succeeds, create a branch with an updated version number</h3>
<p>If we get this far, it means that a new version of bcc has been released, and <code class="language-plaintext highlighter-rouge">action-abuild</code> has successfully built a package from it. This is great news. All that’s left is to create a branch with the changes and notify me so that I can do a final check and submit the package upstream.</p>
<p>I created a second action called <a href="https://github.com/acj/action-branch-from-working-copy">action-branch-from-working-copy</a> to handle this. This action surfaces parameters like <code class="language-plaintext highlighter-rouge">branch_name</code>, <code class="language-plaintext highlighter-rouge">commit_message</code>, and <code class="language-plaintext highlighter-rouge">commit_author_name</code> so that the resulting branch and commit are customizable. Some repositories, such as Alpine’s <a href="https://github.com/alpinelinux/aports">aports</a>, ask package maintainers to use a consistent style in their commit messages (e.g. “community/bcc: update to 0.11.0”), and these parameters make it possible to automate all of that.</p>
<p>As I mentioned before, a few things can happen at this stage. If the new branch (whose name is derived from the version number, e.g. <code class="language-plaintext highlighter-rouge">bcc-to-0.11.0</code>) already exists in the repository, then we have to decide whether to treat this as a success or a failure. Which result is appropriate arguably depends on the workflow that’s invoking the action, and so I’ve added a <code class="language-plaintext highlighter-rouge">fail_if_branch_exists</code> input parameter. There’s a corresponding output parameter called <code class="language-plaintext highlighter-rouge">branch_name_already_exists</code> so that the calling workflow knows whether a successful result means that the action created a new branch or that it did nothing (it’s already there, so you’re good to go).</p>
<p>One lingering question is what to do if the working copy doesn’t contain any changes. The action currently returns a successful exit code in that case. Feedback is welcome, as are PRs.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Create a branch with updated package version</span>
<span class="na">id</span><span class="pi">:</span> <span class="s">create_branch</span>
<span class="na">if</span><span class="pi">:</span> <span class="s">steps.resolve_versions.outputs.have_new_version == 'true'</span>
<span class="na">uses</span><span class="pi">:</span> <span class="s">acj/action-branch-from-working-copy@v1.0.0</span>
<span class="na">with</span><span class="pi">:</span>
<span class="na">BRANCH_NAME</span><span class="pi">:</span> <span class="s">$</span>
<span class="na">COMMIT_MESSAGE</span><span class="pi">:</span> <span class="s">$</span>
<span class="na">COMMIT_AUTHOR_NAME</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Adam</span><span class="nv"> </span><span class="s">Jensen'</span>
<span class="na">COMMIT_AUTHOR_EMAIL</span><span class="pi">:</span> <span class="s1">'</span><span class="s">acjensen@gmail.com'</span>
<span class="na">FAIL_IF_BRANCH_EXISTS</span><span class="pi">:</span> <span class="s1">'</span><span class="s">false'</span>
<span class="na">env</span><span class="pi">:</span>
<span class="na">GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">$</span>
</code></pre></div></div>
<h3 id="notify-me-if-theres-a-new-version-or-a-problem-with-the-build">Notify me if there’s a new version or a problem with the build</h3>
<p>We’ve nearly done it. The new version of bcc has been successfully built, a branch has been created with the updated version number, and it’s ready to be submitted upstream. But how do these robots inform me that any of this has happened?</p>
<p>I’ve chosen Slack as the notification mechanism, but there are many options. If you wanted to take this workflow a step further, it could even create the upstream PR for you. For now, I’m just happy that I didn’t need to fetch the new code, build it, and juggle the version numbers.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Notify Slack</span>
<span class="na">if</span><span class="pi">:</span> <span class="s">steps.resolve_versions.outputs.have_new_version == 'true'</span>
<span class="na">env</span><span class="pi">:</span>
<span class="na">SLACK_WEBHOOK_URL</span><span class="pi">:</span> <span class="s">$</span>
<span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
<span class="s">branch_name="$"</span>
<span class="s">branch_already_exists=$</span>
<span class="s">if [ "$branch_already_exists" == "true" ]; then</span>
<span class="s">curl -s -X POST -H 'Content-type: application/json' --data "{\"text\":\"New version of $ is available, but branch '$branch_name' already exists\"}" $SLACK_WEBHOOK_URL</span>
<span class="s">else</span>
<span class="s">PR_URL="https://github.com/acj/aports/pull/new/$branch_name"</span>
<span class="s">curl -s -X POST -H 'Content-type: application/json' --data "{\"text\":\"New branch for $ created: $PR_URL\"}" $SLACK_WEBHOOK_URL</span>
<span class="s">fi</span>
</code></pre></div></div>
<p>Note that this step doesn’t handle failure cases. If a previous step failed, I’ll get an email from GitHub saying so.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>We did it! And I’m happy to report that this thing has already saved me time and toil. The 0.11.0 release last week was the first real test of my workflow, and – naturally – bcc made a couple of bigger changes that broke the Alpine build. The important thing is that I learned about the build failure immediately and had an upstream PR merged within 72 hours. With a little luck, the next release will build cleanly, and I’ll wake up to a new branch that I can test and publish.</p>
<p>GitHub Actions is a game changer. It’s not the first workflow engine (far from it), and as far as I can tell it’s not enabling anything that we couldn’t already do, but it’ll be the first workflow engine that a lot of folks use. Many won’t even realize that they’re using one. When you grab an action and have it running on your repository a few seconds later, it feels like magic. For example, I added a simple build-test-report CI action to a personal Go project repository recently. The only “configuration” was choosing which <code class="language-plaintext highlighter-rouge">go test</code> arguments to use. The whole thing took less than five minutes, which included several test builds. It shows the true power of this ecosystem – forums, good documentation, easy feedback/remixing, containers, and event-driven architecture.</p>
<p>And what probably matters more than anything else is that it was <em>fun</em> to build this. That’s huge.</p>I recently got access to GitHub Actions through the beta program, and it got me thinking about what parts of my life as both a hobbyist and professional programmer could be automated. Adding continuous integration to a handful of side-project repositories was an easy one. For something juicier, I wondered if it would be possible to automate the often tedious process of updating packages for Linux distributions. I’m the maintainer of a couple of packages for Alpine Linux, and it would be nice to let the machines do the boring parts.progfun 0022013-05-17T00:00:00+00:002013-05-17T00:00:00+00:00https://ehseejay.com/2013/05/17/progfun002<p>I just finished <a href="https://class.coursera.org/progfun-2012-001">progfun 002</a>, a course offered by Coursera that teaches the fundamentals of functional programming in Scala. I was left with a favorable impression of the course and wanted to share some postmortem thoughts.</p>
<h2 id="time-management">Time Management</h2>
<p>This was my first online course. It was also my first post-gradschool course. I spend plenty of free time writing code, but finding a sustainable way to make time for difficult (even tedious) assignments is a different beast. There were plenty of evenings when I didn’t have enough energy to solve hard problems, to say nothing of solving them in an alien language that I was just beginning to understand. The deadlines are a good partial solution to this. You <em>can</em> turn things in late—life happens—but it will cost you. The scaffolding that’s provided in the problem sets is the other half of the solution; I’ll get to that.</p>
<h2 id="lectures">Lectures</h2>
<p>Each week, a half dozen short (10-20 minute) lectures are posted that cover the material for that week’s assignment. (That’s backwards, of course; you’re taking the course to learn the material, not just to solve the assignments. But when you’re five weeks in, you’re thinking about the assignments.) The lectures are nicely parceled into small topics that can be watched during a coffee break or over lunch.</p>
<p>The pace of the lectures is slow and steady. (I watched most of them at 1.25x. Beyond 1.5x, the audio becomes difficult to understand.) The videos are fairly easy to scan when you need to review a segment. I do wish that the course organizers would prepare a set of lecture notes that highlight the main points of each segment for later review. On more than one occasion, I found myself scanning through multiple videos in search of a specific slide. <del>PDF versions of the slides (even without the instructor’s annotations) would be another useful option.</del> As Luc points out in the comments, a PDF version of the slides is available for each lecture. (Thanks!)</p>
<p>Periodically, the lecture will pause and present you with an exercise. <strong>These are probably worth your time.</strong> Most of them, anyway. The exercises might not be an option if you’re watching a lecture during your coffee break, but they provide a helpful check for your understanding and are a good way to cement new concepts in your mental model of Scala.</p>
<p>The lecture material can seem bogged down at times, especially when the instructor is discussing deep connections to mathematics, proofs, or topics from theoretical computation. If you’re a working professional whose aim is to develop a working level of fluency in Scala, such segments are probably safe to skip. For students of computer science or anyone with a bit of extra time, they are worthwhile. Martin Odersky, the course instructor, is a titan in the world of Scala, and he has a knack for distilling complex topics into useful morsels. Take advantage if you’re able.</p>
<h2 id="assignments">Assignments</h2>
<p>Much like other well-known computer science courses (such as CS50X), this course unabashedly delivers some pain. The assignments are mostly excellent, though, and the time investment will leave you ready to build useful software.</p>
<p>As I mentioned earlier, the assignments provide a lot of <em>scaffolding</em>—code and documentation that is incomplete but provides a rough path for you to follow. The scaffolding serves two purposes in a course like this: it provides a skeleton for the solution that you’re developing, and it enables you to solve real, revelant problems without the cognitive load of simultaneously designing <em>and</em> implementing a solution.</p>
<p>The assignments also provide small fragments of finished code that can be used as-is. These are often fragments of code that are uninteresting, easy to screw up, or both. Mostly they’re irrelevant to the problem at hand, and it’s a better use of your time to focus on the stuff that matters. The course designers have clearly embraced this.</p>
<h2 id="discussion-boards">Discussion Boards</h2>
<p>One of the great benefits of an online course is the discussion that happens around the lectures and the assignments. Even greater is being able to search those discussions after they’ve happened. Coursera has good infrastructure for supporting rich conversations, and this course in particular has a wonderful group of staff TAs who are available (seemingly at all hours) to answer questions. It’s often tempting to give and receive too much advice on the discussion boards, but with a bit of self-discipline you should be able to glean just enough information to see the solution.</p>
<h2 id="automatic-grading">Automatic Grading</h2>
<p>The auto-grading machinery for this course is well done. Assignments can be submitted multiple times without penalty, and generally the grader will provide enough feedback that you can identify and resolve any problems with your code. On one occasion, though, I strongly wished that the test inputs (strings) were provided along with the grader’s output. The feedback can be cryptic. For most assignments, in my judgment, providing the test inputs would not give an undue advantage to anyone.</p>
<h2 id="time-investment">Time Investment</h2>
<p>The course description predicts that you’ll need 5-7 hours per week. I think that this is slightly misleading. You can expect roughly one hour to be consumed by lectures. The time spent on assignments, however, will vary significantly—especially if this is your first experience with functional programming. I finished one assignment in less than 30 minutes, but another took me more than 10 hours. The genuine feeling of enlightenment is worth the effort, but it will take some time. The later assignments are fairly complex, and you will need to find additional time for debugging. Learner beware!</p>
<h2 id="coda">Coda</h2>
<p>If you have any interest in functional programming, I hope that you take this course. It’s accessible to anyone with a good grasp of programming, and you’ll be hard pressed to find another course of equal calibre. For me, though, this is the real test: after working through the assignments, I feel prepared to learn the rest of its faculties and get my hands dirty with some real projects.</p>
<p>There are efforts underway to make a follow-up course that will cover concurrency and other advanced topics.</p>I just finished progfun 002, a course offered by Coursera that teaches the fundamentals of functional programming in Scala. I was left with a favorable impression of the course and wanted to share some postmortem thoughts.How to hack your garage door opener2013-04-14T00:00:00+00:002013-04-14T00:00:00+00:00https://ehseejay.com/2013/04/14/hack-your-garage-door-opener<p>It’s well documented that the INSTEON <a href="http://www.smarthome.com/2450/IOLinc-INSTEON-Low-Voltage-Contact-Closure-Interface-1-In-1-Out-/p.aspx">I/O Linc</a> can be used to control a garage door and monitor whether the door is open. It can even be accessed from a mobile device, making this a highly convenient home automation tool for less than $100. What was far less clear (to me, at least) was exactly how to connect the I/O Linc to the garage door opener. So, what follows is equal parts tutorial and lessons learned. The details will almost certainly vary, so be sure to consult the user manuals for your equipment.</p>
<h2 id="equipment-list">Equipment List</h2>
<ul>
<li><a href="http://www.smarthome.com/2412N/SmartLinc-INSTEON-Central-Controller/p.aspx">SmartLinc 2412n controller</a></li>
<li><a href="http://www.smarthome.com/2450/IOLinc-INSTEON-Low-Voltage-Contact-Closure-Interface-1-In-1-Out-/p.aspx">I/O Linc</a></li>
<li>20-28 gauge wire</li>
<li>Garage door opener</li>
<li>Magnetic sensor (optional)</li>
<li>Soldering iron, wire nut, or some other means of joining wires (optional)</li>
</ul>
<h2 id="wiring-the-garage-door-opener">Wiring the Garage Door Opener</h2>
<p>A garage door opener (GDO) typically has several terminals on its rear panel:</p>
<p><img src="http://content.screencast.com/users/a.jensen/folders/Snagit/media/bb483a31-b2d9-4613-bc0a-05670568a2a7/2013-04-13_21-03-40.png" alt="Terminals on garage door opener rear panel" /></p>
<p>Some GDOs will have three terminals; others have four. For three-terminal openers, there are two “hot” terminals (one for devices that control the door, one for safety sensors) and a shared “common” terminal that’s normally in the center. A four-terminal GDO will normally have two terminals for controlling devices and two for sensors. The specific function of these terminals can vary from one GDO to the next, so be sure to track down the manual for your unit. (I hope you have better luck than I did.)</p>
<p>On my GDO—a Craftsman from circa 1997 with part number 41A5021-3B on the rear panel—there are three terminals:</p>
<p><img src="http://content.screencast.com/users/a.jensen/folders/Snagit/media/fe473009-3cfd-4713-b908-7dd089cfb501/2013-04-13_20-55-50.png" alt="Craftsman garage door opener rear panel with terminal callouts" /></p>
<p>To connect my GDO to the I/O Linc, I used a length of old (read: ugly) speaker wire that I had lying around. (I recommend using bell wire or one pair of wires from an RJ-45 cable as they will be a better fit for the I/O Linc terminals.) One wire connects to the Remote/Opener Terminal (#1). The second wire connects to the common terminal (#2) in the center. The button on the wall will also be wired to these terminals.</p>
<p>Next, we’ll connect the other end of the new wires to the I/O Linc. The instruction sheet that ships with the I/O Linc is helpful and full of dense information, but it left me scratching my head. There is one very important wrinkle that must be dealt with before we can wire the I/O Linc to the GDO. The I/O Linc ships with its relay in <em>latch mode</em>, which means that when you press the button on the wall, the I/O Linc will close the relay and <em>keep it closed</em>. Most GDOs assume that the relay will close for a brief period (say, two seconds) and then open again. We need to configure the I/O Linc to respect this assumption.</p>
<h2 id="configuring-the-relay--mode">Configuring the relay mode</h2>
<ol>
<li>Press and hold the “Set” button (on the side of the I/O Linc) until it beeps. Release the button. The I/O Linc is now in <strong>linking mode</strong>.</li>
<li>Press and hold the “Set” button (on the side of the I/O Linc) until it beeps. Release the button. The I/O Linc is now in <strong>unlinking mode</strong>.</li>
<li>Press and hold the “Set” button (on the side of the I/O Linc) until it beeps. Release the button. The I/O Linc is now in <strong>output relay programming mode</strong>. The unit automatically rotates to the next mode; if you were starting with a stock I/O Linc in “Latch” mode, it is now in “Momentary A” mode. This means that an “On” command will operate the door, and an “Off” command will be ignored. (If you prefer that “Off”—or even both commands—will trigger the door, then feel free to explore the “Momentary B” and “Momentary C” modes.)</li>
</ol>
<h2 id="io-linc-wiring">I/O Linc Wiring</h2>
<p>We’re ready to connect the GDO to the I/O Linc. The other ends of our wires need to be connected to the Normally Open (N/O) and Common (COM) terminals on the I/O Linc:</p>
<p><img src="http://content.screencast.com/users/a.jensen/folders/Snagit/media/861375c2-ba58-42e0-bcd3-293f8cc1e3e0/2013-04-13_23-02-53.png" alt="I/O Linc Wiring" /></p>
<p>The other two wires in the photo are for a magnetic garage door sensor. More on that in a moment.</p>
<p>When the I/O Linc receives a signal from its controller (a <a href="http://www.smarthome.com/2412N/SmartLinc-INSTEON-Central-Controller/p.aspx">SmartLinc 2412n</a> in my case), its relay closes, and the normally open circuit is closed for a brief moment. This is the very same event that occurs when you press the button on the wall. The GDO responds by opening, closing, or halting the garage door.</p>
<h2 id="monitoring-the-door-state">Monitoring the door state</h2>
<p>If you want to monitor whether the door is open or closed, then you’ll need a <a href="https://www.google.com/search?q=magnetic+door+sensor">magnetic door sensor</a>. These are inexpensive (< $5) and easy to install. Once the sensor is mounted, connect one wire to the I/O Linc’s “Sense” (S) terminal and the other to “Ground” (GND). When the door is closed, the green Sense light on the I/O Linc will be illuminated. When the door opens, the light will go out.</p>
<p><strong>N.B.</strong>: If you plan to use the INSTEON mobile app, be advised that the “Status” for the I/O Linc is the status of the <em>relay</em>, not the door. When you open or close the door, the status will briefly change from Off to On and then back. If you want to monitor the open/closed state of the door, you will need a more sophisticated app. For Android devices, <a href="https://play.google.com/store/apps/details?id=com.mobileintegratedsolutions.mobilinc.lite">MobiLinc</a> is a good option.</p>
<h2 id="closing-thoughts">Closing thoughts</h2>
<p>Controlling and monitoring your garage door opener with INSTEON devices is not complicated, but pulling together the relevant instructions can be frustrating. Makers of garage door openers generally don’t explain how to do one-off installations, and the dense instruction manuals for the other components can be offputting. Hopefully I’ve found a comfortable middle ground with this tutorial. If you follow these steps and find that the steps are incorrect or that your equipment differs from what I’ve described, please leave a comment below. Good luck, be safe, and have fun.</p>It’s well documented that the INSTEON I/O Linc can be used to control a garage door and monitor whether the door is open. It can even be accessed from a mobile device, making this a highly convenient home automation tool for less than $100. What was far less clear (to me, at least) was exactly how to connect the I/O Linc to the garage door opener. So, what follows is equal parts tutorial and lessons learned. The details will almost certainly vary, so be sure to consult the user manuals for your equipment.Using git to manage text documents2012-01-22T00:00:00+00:002012-01-22T00:00:00+00:00https://ehseejay.com/2012/01/22/git-document-diff<p>Anybody using git to manage text document revisions might benefit from this. I keep all of my research papers in git. I also collaborate on paper writing fairly often, and I may want to see the difference between two revisions.</p>
<p>My first solution looked like this:</p>
<p><code class="language-plaintext highlighter-rouge">git diff --word-diff commit1 commit2</code></p>
<p>This works, but long lines—which are very common in text documents—run off the edge of the terminal window. On Linux and Mac, the default pager for viewing diffs is <em>less</em>, and its stock viewing mode leaves something to be desired. I would like the long lines to be wrapped.</p>
<p>My next attempt was as follows:</p>
<p><code class="language-plaintext highlighter-rouge">git diff --word-diff commit1 commit2 | fmt</code></p>
<p>This version solved the line-wrapping problem, but the result was ugly and difficult to scan. git and fmt don’t play well together. If possible, I would like the changes to be color-coded rather than delineated by some ugly text scheme. Although git’s support for color highlighting works better for code than long-form text, it’s usually good enough that you can work out what changed.</p>
<p>Version 3:</p>
<p><code class="language-plaintext highlighter-rouge">git config core.pager 'less -+$LESS -FRX'</code></p>
<p><code class="language-plaintext highlighter-rouge">git diff --word-diff=color commit1 commit2</code></p>
<p>Bingo: lines wrap properly, changes are color-coded, and no junk text is introduced by incompatible tools. If you want to use the same pager settings across all of your git repositories, add the “–global” flag to the first command.</p>Anybody using git to manage text document revisions might benefit from this. I keep all of my research papers in git. I also collaborate on paper writing fairly often, and I may want to see the difference between two revisions.Hello, World!2011-10-10T00:00:00+00:002011-10-10T00:00:00+00:00https://ehseejay.com/2011/10/10/hello-world<h1 id="a-brief-introduction">A brief introduction</h1>
<p>I’ve long wanted a convenient place to post snippets, anecdotes, and other code- or engineering-related things that I have found useful in some way. This blog will start as that place, and we’ll see where it takes us.</p>A brief introduction