Better Parenting Time Exchanges with TRMNL

A couple of weeks ago I received my TRMNL order, and TRMNL promptly held a perfectly-timed hackathon for calendar-based plugins, lighting the proverbial fire under my behind to get me to write a plugin to show my kids how long until they go to their other parent’s house, and to give me a countdown until they’re back. While I didn’t win the hackathon, (check out the winners, they’re great), I continue to use and improve my little plugin with the goal of eventually turning it into a public recipe. I am certain there are plenty of parents out there in the same situation that I am, or who simply want to count down to other recurring calendar events.

A screenshot of the TRMNL device showing my private countdown plugin full screen.
This is what the full-screen version of my Calendar Countdown plugin looks like.

Things to Know When Coding for TRMNL

Debug JavaScript Right In Your Browser

TRMNL’s web-based coding environment is a bit weird, but it runs Javascript in the browser, which means that console.log statements appear in your browser’s dev tools.

Watch Out for the Time Zone!

When dealing with Date/Time in Javascript, the device’s timezone might not match your timezone. The user timezone data that supplied in the trmnl.user data should be used to set the appropriate timezone.

Events Not in Chronological Order

Sometimes calendar events don’t appear in chronological order in the data feed. I rectify this by running an Array.prototype.sort() on my array of interesting events once I have identified them.

Wishlist to make TRMNL Even Better

More Data Types for Settings

The ability to have a dictionary or object in settings, with a UI to set key-value pairs, would make it easier to have a list of events that are interesting to the plugin.

Improved Story for Local Development

While the online development system works, I would like a more robust system for local development. There is trmnlp, which is supposed to give a local development environment, but I was never able to get it up and running. I suspect that using trmnlp would allow me to put my Javascript into its own file, not have to copy the same Javascript into the HTML of all four of the layouts for my plugin.

Gallery Captions for WooCommerce 1.10 – Maintenance & Compatibility Release

After a long pause in releasing, I just got Gallery Captions for WooCommerce version 1.10 up on WooCommerce.com. This update brings compatibility with YITH WooCommerce Product Gallery & Image Zoom, changes the way compatibility code is loaded, and bumps the “tested up to” versions of WordPress and WooCommerce to the most recent versions.

Because the last release was in late 2023, this release was my first time using WooCommerce’s Quality Insight Tests. My first impressions: they take longer than the old tests, but give more detailed feedback – I like them!

If you want to have captions under the images in your WooCommerce store, and have somehow landed here before WooCommerce.com, head on over and install Gallery Captions for WooCommerce. It has great reviews and I provide great support!

Cloudflare Workers for Fast, Inexpensive, Lightspeed X-Series Business Rules

A client that operates in a highly-regulated field needed a Business Rule for their Vend / Lightspeed X-Series POS system, and we were able to implement the rule using a Cloudflare worker, making it blazing-fast and extremely reliable, while also reducing the initial cost by 90% and essentially eliminating ongoing costs. It felt great to help improve their business, and I still was paid well for my time, so this was a good outcome all around. I enjoyed it enough that I would like to do more, so if you need Lightspeed Retail X-Series Business Rules please contact me!

A note on terminology: The POS system was created by Vend, which was recently bought by Lighspeed. Rebranding isn’t complete so the product is referred to as both “Vend” and “Lightspeed Retail POS (X-Series)” – which is quite a mouthful. I am used to saying “Vend” so that’s probably what I’ll use the most, but may also use “Lightspeed X-Series” for future SEO.

What are Lightspeed X-Series Business Rules?

Business Rules are web hooks that the Vend platform sends when certain events take place. Depending on the response to the web hook Vend can take certain actions, such as showing a message to the cashier, preventing the sale, and more. Because the POS waits for the rule to finish before continuing rules need to send their response extremely fast, otherwise the Point of Sale system will feel super-slow and broken. Lightspeed X-Series will wait up to 2 seconds for a response from the URL configured, but every effort should be made to respond faster, especially if rules are going to be run when adding items to the purchase. Imagine if waiting two whole seconds between scanning items at the cash!

Cloudflare Workers are the Tool for the Job

The rule we created was able to make all decisions based on the contents of the cart, so when considering how to respond super-fast to a web request containing all of the information needed we realized that Cloudflare Workers are perfect for the job: they’re fast, easy to deploy, and inexpensive.

I also got to work with modern Javascript without worrying about browser compatibility, and that was a breath of fresh air.

Cost Savings

The client had previously set up a similar business, but used a vendor based in the USA to create the business rule. My understanding was that the cost was around US$5,000 to create the rule, and there was an ongoing cost of around US$25 per month per store. With several stores that was an ongoing bill of a few hundred dollars per month.

My work is part of a larger engagement, but it only took me a few hours to learn to build a Cloudflare Worker and program the logic, and both I and the client are in Canada, so my total bill for setting up the business rule was just over 500 Canadian Dollars, savings of over 90%. The worker is running in the client’s Cloudflare account and falls within the limits of the free Cloudflare worker plan, so the ongoing cost is zero. In the future if they grow and exceed the free limits Cloudflare’s worker pricing is essentially a rounding error in the scale of their business.

My CA$500 bill doesn’t include future modifications, so the client may choose to spend more money in the future fine-tuning the rule, or changing the rule as their business needs change, but I don’t see how the total cost could possibly exceed the competing solution.

Business Rule Wishlist

Vend/Lightspeed’s business rules provide an interesting way to extend the Point of Sale system in unique ways. In our situation, because of regulations, we need to limit the amount of certain products that can be purchased in a single transaction – something that I wouldn’t expect to find included by default in a POS system, but so much more is possible. That said, I have a wish list of improvements for business rules. Lightspeed, are you listening?

  • I have been told the POS has an offline mode for situations where internet isn’t available. I would like to see some sort of runtime available to run rules locally. This would let us enforce business rules even if the store’s internet is offline, (some of the client’s stores are fairly remote).
  • Run the “stop” command, or at least the “confirm” command, (which shows a message to the cashier), when adding line items. This would let us prevent an item from being added to the sale, (or let us remove it, then tell the cashier what happened), if the addition of the line item would go over the regulatory limit.

Could I build a business on this?

Building this business rule has me wondering if I could build a business on Lightspeed Retail POS (X-Series) business rules. I’m not sure exactly how it would work, but I could imagine providing access to a library of commonly-needed, pre-built rules for a reasonable flat fee, or maybe a usage-based fee. Maybe custom rules could be built for an hourly rate then run for that same usage fee. There’s some thinking to do! In the meantime I’m interested in building more of these, so if you need business rules, for Vend or any other system, get in touch.

Laravel Envoyer Notifications in Zoho Cliq

I recently switched a project from a home-grown deployment script to Laravel Envoyer. While the homegrown script could maybe have been adapted it would have taken time, and had to be maintained, and Envoyer offers some useful extras like Slack, Discord, and Microsoft Teams integration, and heartbeat monitoring.

Except we don’t use Slack, Discord, or Teams, we use Zoho‘s Slack competitor, Cliq.

Comparing Slack’s web hooks with Cliq we see that a bot is needed in Cliq, but making one is super-easy, on the order of four or five clicks.

Once there is a bot it can receive web hooks. Figuring out the right way to provide an authentication token was weird, there is not clear documentation, but the reply from Eric Hirst in in the Zoho forum thread announcing Cliq bots has a solution that works.

Now we can receive web hooks, I tried putting the bot’s web hook URL in for both Slack notifications and Discord notifications in Envoyer, and I got notifications. Both systems expect a POSTed JSON object, so that’s what they get. The Discord one is simpler one of the properties is simply markdown text, which Cliq understands, so the Cliq incoming web hook handler needs to grab that markdown text and return it so the bot will post the message in Cliq.

// Configure the bot's incoming webhook URL in any third-party service to trigger this handler.
response = Map();
message = body.toMap();
if(message.containKey('content'))
{
	response.put("text",message.get('content'));

	// If you want the bot to post to one of your channels, and want it to appear
	// as the bot posting, add the bot info like this. Otherwise it the message 
	// will be "from" the person who owns the auth token, with a small "bot" flag next to it.
	response.put('bot',{"name":"Envoyer","image":"https://envoyer.io/img/favicons/apple-touch-icon-120x120.png"});

	// If you want to post to a channel this is how. If you don't do this the bot
	// will simply post the message to any chats it has open.
	zoho.cliq.postToChannel('general',response);
}
return response;

The beauty of simply dumping the markdown into the chat is that this simple code handles all notifications from Envoyer, including successful & failed deployments and heartbeat notifications. You can look for specific text and alter your notification if you want, (I have a big headline for successful deployments), but it’s not needed.

It would be easier to test this if there was a way to trigger a test notification in Envoyer, but Envoyer isn’t really something to tinker with, it’s supposed to just work. It would also be great if Envoyer could support Zoho Cliq directly, but I’m not sure many people in the Laravel community are using Zoho.

So now we have Envoyer notifications in Zoho Cliq in our organization. If you’re one of the few Laravel / Zoho unicorns out there like me you can have them too.

One-Click DevonThink Markdown Journal Entry

With COVID-19 running wild here in Quebec we are homeschooling this year. One of the ways that the government evaluates the progress of homeschool students is by asking parents to submit a portfolio. Since I’m primarily responsible for English classes I need to have records. I already use DevonThink as a data repository, so that’s where the homeschooling records are going.

English Journal

Every time we spend some time working on English I create a journal entry as a new Markdown in Devonthink. I thought about keeping one large document and continually appending to it but several small documents seems more searchable and gives me accurate timestamps and geolocations, (if we ever travel again). The journal entries are all kept in a Homeschool > English Journal group.

Since creating a new markdown document several times a week is kind of slow I made a template, and added a button to the menu bar in Devonthink to create a new English Journal entry with one click.

Creating a Devonthink Template

As a fairly new Devonthink user this was my first foray into using templates. I assumed there would be some sort of template editor in Devonthink but there isn’t. You can create an existing document as a template or copy an existing template in Finder. There are also two kinds of templates: normal, and “smart” templates. Normal templates aren’t completely dumb – they have some placeholders that can be replaced by dynamic values, (things like Date, Time, or the user’s name). Smart templates are a bundle of files, including an AppleScript file. The AppleScript file is the main file in the template and controls everything. There can be other files in the bundle, (like a template.md file), and the AppleScript file can refer to those files. I ended up with a Smart template, with some minimal smarts.

A screenshot of the directory structure of the English Journal Devonthink template.The script in my template opens my “Home” database, makes sure the Homeschool > English Journal group exists, then creates a new markdown document in the English Journal group based on the English Journal.md file. The script is relatively simple:


-- Import helper library
tell application "Finder" to set pathToAdditions to ((path to application id "DNtp" as string) & "Contents:Resources:Template Script Additions.scpt") as alias
set helperLibrary to load script pathToAdditions

-- Get the template file path.
set theTemplateFile to helperLibrary's pathToLocalizedResources() & "English Journal.md"

tell application id "DNtp"
	
	
	-- Open the database.
	set theDatabase to open database "/Users/John/Databases/Home.dtBase2"
	-- Get a reference to the group I want.
	set theLocation to create location "/Homeschool/English Journal" in theDatabase
	
	-- Create the document based on the template file.
	set entry to import theTemplateFile to theLocation placeholders {}
	
	-- Open the new document.
	open tab for record entry
	
end tell

The markdown file is pretty simple too. %time% and %longDate% are Devonthink placeholders to put the date & time into the journal entry.

# English Activity Record
## %time% %longDate%

### Activities
- Pages ### - ### in _Toute ma 3e année_.

### Parent Reading Aloud

I may adjust it to prompt for a title for each entry, and to add my current location to the text of each entry.

One-Click Template Use

With a working template bundle, (in ~/Library/Application Support/DEVONthink 3/Templates.noindex), it was time to put a button in the Devonthink menu bar:

How to put a template in the Devonthink toolbar:

  1. Move the template bundle into ~/Library/Application Support/DEVONthink 3/Templates.noindex/Toolbar
  2. Restart Devonthink
  3. Go to View > Customize Toolbar in Devonthink
  4. Drag the “English” button to the Toolbar.

While View > Customize Toolbar is open you can choose to show the Icon and Text in the toolbar if you want.

Set an icon for the toolbar button:

It is possible to set a custom icon for the toolbar button, (by default it’s a gear). Devonthink has a weird way of setting the icon, (weird in a good way): set the icon of the template bundle in Finder. Devonthink takes whatever icon Finder thinks the template should have and puts it in the toolbar.

How to customize a file’s icon on macOS:

  1. Open the image you want to use as a custom icon, (in Preview, or wherever).
  2. Copy the image, (Command-C, or Edit > Copy).
  3. Option-click the file that will get the custom icon.
  4. Select “Get Info” from the menu.
  5. Click the file icon in the “Get Info” window so it’s highlighted.
  6. Command-V to paste the image you copied in Step 2 as the custom icon.

If you ever want to remove the custom icon to back to the Get Info window and Command-X to remove it.

The new “English” button in my Devonthink.

Mobile Entry?

This system only works on my computer. I’d like to have a mobile option but in this moment it’s not a pressing need.