Running Nextcloud + Collabora on TrueNAS SCALE with Nginx Proxy Manager

For a while, I have been having trouble setting up Nextcloud services in my TrueNAS box, the difficulty being that unlike most TrueNAS SCALE apps, Nextcloud often requires additional apps to provide additional services (in this case, an office suite by Collabora). Managing the routing between different apps and accessing them through a reverse proxy (Nginx Proxy Manager) has been quite confusing for me. I need to make this work with the least amount of hacks possible, as I am neither an expert in Docker deployment nor do I have time to debug errors on every update. Interestingly, compared to programming, when it comes to TrueNAS setup (and my home network setups), I am exactly the opposite of a hacker 😬. I do not want to tinker with them at all. I'd much prefer to set it up once and have it run for years (auto-update of course).

Data-oriented workflow with OpenUSD Hydra

Fancy rendering techniques dominates the content of computer graphics blogs. Today, though, I am not going to talk about the drawing itself, but about the "boring" part: the bridge that turns assets: (in my case USD hydra) per-prim scene graph into the flat, GPU-ready arrays my batches want. For the past year that bridge was the ugliest code in the project – three overlapping bookkeeping mechanisms, data processed one prim at a time, and a lock around almost every access.

A few thoughts on AI

I originally planned to write this post in 2024, during the GPT-4 and Claude 3.5 era. Back then, I titled it "Reflections on a Career in the Age of AI". I figured any opinions I wrote then would likely look foolish by now. We went from the hype of prompt engineering to MCP/tool use, and now to agentic AIs. At this point, any conclusions I draw will probably be outdated in a few months. Still, for entertainment value, I'll do my best not to embarrass myself too much in the future.

Building a quadrature encoder water meter with ESPHome and magnetometers

Recently I have been spending time on a magnetometer based smart water meter for Home Assistant. The idea is simple: strap two magnetometer sensors (QMC5883L) onto a residential water meter, read the rotating magnetic field, and count water flow. What actually happened was a 10 nights debugging odyssey that taught me about multi-pole magnets, quadrature encoding, and the joy of writing actual C++ for ESPHome.

This project is a fork of tronikos/esphome-magnetometer-water-gas-meter, which already does single-sensor flow counting with a calibration UI. I was using this initially on my meter, for what it advertises, it worked pretty well. But ever since I heard about the benefits of quadrature encoding from espleak. I couldn't help want to upgrade to it. It was scheduled since last September but procrastination beat me so hard on this one.

Automated Vulkan Synchronization

Recently I finally decided to solve the synchronization system in my Vulkan renderer. It is something that I really want to do for a long time, to me it should be part of the FrameGraph design in the application, where you declare your individual render passes and the resources it uses (read/write). The synchronization system itself handles the resource transitions, where it should change layout from? Whether we need to setup a semaphore etc.

ESPHome, emporia vue, winter, 2025

We had a incredible snowy winter this year in Toronto. 25cm of snow in 24 hours on Sunday [2025-02-16 Sun], which preceded by 20+cm earlier Thursday. While stuck at home, I found sometime to work on some long delaying projects. This got me into ESPHome. Yeah, home automation, somehow I feel this is probably inevitable for north American dwellers.

This days, the smart home word is incredibly messy, on the one hand, you have those giant players like Google home, Apple HomeKit, and Amazon Alexa. On the other hand, all the IoT manufactures are trying to steal user data by their crappy app. water valves, light switch, garage door openers. All of them are trying to lock you to their app. For you to simply close your garage, you have to send a command on your phone to their proprietary cloud server, then travel all the way back to connect to your opener at home. This is insane! Hopefully with the new Matter standard, it may bring some sanity back.

Event bus implementation, Part II, on a task system

Recap on EBus

From our last post on an eBus implementation. We talk about that how you can implement the Observer pattern so your can decouple the code from the callers to its callees. The perfect use case for that is obviously to register the observers for certain events such as ticks,

For example, a keyboard event listener may looks like this:

  //the listener will override those methods
  struct keyboard_input_listener : public ebus_handler<keyboard_events>
  {
          virtual void on_enter(keyboard_t* keyboard,
                                surface_t* surf, uint32_t key) override;
          virtual void on_leave(keyboard_t* keyboard, surface_t *surf) override;
          virtual void on_key(keyboard_t* keyboard,
                              uint32_t key, uint32_t state) override;
          //other events ...
  };
  //then listen on the events.
  keyboard_input_listener listener;
  listener.connect();

  //certain point a system will trigger events
  ebus<keyboard_events>::event(&keyboard_events::on_enter, ...);
  ebus<keyboard_events>::event(&keyboard_events::on_key, ...);
  ///...
  ebus<keyboard_events>::event(&keyboard_events::on_leave, ...);

But actually the power of the eBus is far beyond this. On top of this system, we can build other system as well. In this post, we are going to build a (async) task system.

Customizing Hide-Show for CMake Mode in Emacs

customizing HideShow mode on Emacs is done by hs-special-modes-alist. There are 4 parts (actually 5, but we care mostly about the 3) of the equation. You can customize it for your mode with a list like this

  (MODE START END COMMENT-START FORWARD-SEXP-FUNC ADJUST-BEG-FUNC)

For example, the default definition of the hs-special-modes-alist looks like this:

  (defvar hs-special-modes-alist
    (mapcar #'purecopy
            '((c-mode "{" "}" "/[*/]" nil nil)
              (c-ts-mode "{" "}" "/[*/]" nil nil)
              (c++-mode "{" "}" "/[*/]" nil nil)
              (c++-ts-mode "{" "}" "/[*/]" nil nil)
              (bibtex-mode ("@\\S(*\\(\\s(\\)" 1))
              (java-mode "{" "}" "/[*/]" nil nil)
              (java-ts-mode "{" "}" "/[*/]" nil nil)
              (js-mode "{" "}" "/[*/]" nil)
              (js-ts-mode "{" "}" "/[*/]" nil)
              (mhtml-mode "{\\|<[^/>]*?" "}\\|</[^/>]*[^/]>" "<!--" mhtml-forward nil)
              ;; Add more support here.
              ))

The ingredients

START and END regular expression

This regular expression matches the beginning/end of the folding code. For C/C++ alike languages, this is as brackets "{", "}", etc. For some language lacks of any brackets/parenthesis like python, it is more difficult. Emacs' built-in python mode provided patch to support it:

Event bus implementation

Recently I open sourced a small C++ library called eBus(Event Bus), it came along separated from another project I was working on. The idea, or should I say the interface was not very original, I took the inspiration from a much more complicated Event Buses In O3DE. The goal is simple, providing an easy to use Observer pattern so your can decouple the code from the callers to its callees.

Instead of doing this:

Modular Vulkan feature and extension manager

Recently I've been trying to squeeze a few hours at a time from my weekends to work on my Vulkan renderer projects. Because the extremely limited time I have, any feature I want need to planned well and get implemented in a few hours or I need to break it down to do so. One of such feature I've want to implement is a modular Vulkan Feature management.

Root Issue: hard-coding enabled features

When creating an Vulkan device, you have a lot of options which extensions you want to enable, which feature you want to enable. This is done by inserting features into the pNext chain and extensions into ppEnabledExtenionNames.