Reolink

Recovering readable C++ from Reolink's Apple Silicon SDK

I wanted to have a go at making my own ARM64 port of Reolink’s macOS app. They had not released one yet, and getting there first would have been fun to show off. Spoiler alert: they beat me. Oh well.

I’m doing this with the assistance of Codex. I supplied the goals and directed the work; Codex did the reverse engineering, implementation, and comparison tests described here.

I was also curious why an Electron app was still waiting for an ARM64 version. It turned out there was much more native code involved than that description suggested. The interface uses Electron, but communication with Reolink’s cameras and NVRs goes through a native C++ SDK, with its own device state transitions, encryption, media handling, configuration serializers and callback machinery. Porting that meant understanding considerably more than the interface.

Reolink beat me to releasing an ARM64 version. The work has continued, now using the official Apple Silicon binaries as a reference for reconstructing readable C++.

I still want a thorough understanding of what Reolink’s hardware and SDK can do, and a way to investigate some of the crashes in the official macOS app. Understanding the implementation might make those crashes easier to diagnose and, hopefully, fix.

A readable reconstruction could also help with Home Assistant integrations and custom apps. That connects to my Home Assistant camera-announcement work and the Guard Dog Android TV app and its Swift macOS sister app, Sentry. Understanding the native SDK could help explain device behavior and expose capabilities useful to those projects, even while the replacement is unfinished.

Recovering one small method can expose enough of the surrounding contract to make the next one approachable. One of the recurring problems is deciding what “recovered” actually means. A function can return the expected number while getting ownership, callbacks or object layout wrong in ways that only show up later.

C++ symbols help more than I expected. When the binary exposes a mangled name, it provides clues about the class, namespace, arguments and qualifiers. Codex reconstructs declarations that make the compiler naturally emit the same name. A convenient exported alias can keep a boundary callable, but it leaves unanswered questions about the actual declaration behind it.

The tests need to follow those questions. For a configuration callback, I care about the exact XML bytes and response fields. For encryption, the output buffer and error handling matter alongside the return value. For an object destructor, I need to know which resource is released, which state is cleared and which object owns the final deletion.

One recent example was the encryptor’s lifecycle. Codex recovered the RSA resource ownership and separate destructor variants to replace a minimal destructor with ordinary C++. Constructors had their own initialization and layout checks. Those are small-looking functions with consequences beyond whether the immediate test call crashes.

Device opening is more involved. Codex reconstructed and compared portions of the policy and state-machine chain, including transitions through opening and login states. Some checks use fixture-owned callbacks or inert transport boundaries to exercise ordering and return mapping repeatedly. That proves the covered boundary contract; the real network implementation and a successful live device session need their own evidence.

Codex also compares compiler output with the original ARM64 instructions. That catches a different set of mistakes from behavior tests: missing branches, argument-order errors, unexpected memory writes or an incorrect calling convention. Compiler differences can remain even when a readable implementation has useful behavior and ABI evidence. The comparison notes record that drift. I want the implementation to stay readable, even when the remaining mismatch lowers its score.

By early October, the work had moved further into natural callback-operation classes and configuration dispatch, with focused runtime, symbol, object-layout and generated-code checks. Splitting implementations into source files and testing their default build path has been useful too. A standalone test can accidentally supply a helper that the actual replacement doesn’t own.

The replacement SDK is still unfinished. Partial builds have unresolved symbols, mocked boundaries remain, and successful local comparisons don’t establish a complete working transport or desktop client. I keep those categories separate when counting progress, because a tested wrapper around an unknown implementation is still an unknown implementation.

The useful output so far is a growing body of understandable C++, plus checks that explain why I believe particular pieces match. There’s plenty left to recover. At least the next investigation can start from an explicit contract, with the remaining unknowns written down.

12:00 am / Reverse engineering , Reolink , Cpp , Arm64 , Codex , Ai assisted

Guard Dog and Sentry: watching doggies with Reolink on Android TV and Mac

The house I’m in has an extensive Reolink camera system, inside and outside. We’re keeping an eye on three doggies as they move between those spaces. Reolink’s desktop app didn’t automatically switch to the cameras with activity, and I found it a bit crashy in my use. I wanted the views to follow wherever the dogs were hanging out without having to keep switching cameras myself.

So I built two sister apps with the assistance of Codex:

  • Guard Dog, a Kotlin app for the living-room Android TV, gives everyone in the room a view of what’s happening.
  • Sentry, a native Swift app for my Mac, lets me watch the doggies while I’m working, including in Picture in Picture.

I supplied the goals and directed the work; Codex did the investigation and implementation described here. Codex also drafted this post.

Choosing the views

Both apps use local Home Assistant activity events to choose which cameras to show, with a direct event connection for one standalone camera. Video goes straight from the cameras or NVR to the viewing app. Home Assistant supplies the activity information without carrying the video.

Up to four cameras appear at once. Already-visible cameras keep their positions when another view arrives, and a cleared detection gets twenty seconds of grace. When everything goes quiet, the last active camera stays on screen. A dog briefly falling out of the camera’s detection zone shouldn’t make the whole layout collapse and reform.

[... 859 words]

12:00 am / Android , Macos , Swift , Reolink , Home assistant , Video , Codex , Ai assisted

My security camera tells me when the laundry is done

I wish Reolink did more with the speakers in its cameras. They’re already powered, on the network, and sitting in useful places. They could double as little smart speakers for announcements, but that use doesn’t seem to get much attention.

I got these camera announcements working with the assistance of Codex. I supplied the ideas and directed the work; Codex did the technical investigation and implementation for my integration fixes and automations. The existing integrations are credited below. Codex also drafted this post.

I’d already opened an issue about adding speaker playback to the library behind Home Assistant’s official Reolink integration. I decided to have Codex investigate getting it working for my setup.

I wanted to know when the washer or dryer finished, even from across the house with the laundry-room door closed. There was already a Reolink camera with a speaker next to the stairs, near the bedrooms and a bit away from the machines. That seemed like a useful place for the announcements to come from.

First I needed Home Assistant to know when the appliances finished. Samsung had announced a $4.99/month plan for SmartThings API access, with free access being phased out starting in October. I wasn’t about to pay a stupid subscription for that. I used LocalThings instead: a free, open source integration that talks to the washer and dryer directly over the local network.

I still had to connect the appliances to SmartThings first to get LAN access working, and keep them registered there. LocalThings handles Home Assistant’s ongoing communication with them locally.

The official Reolink integration still lists two-way audio and text-to-speech as unavailable, so I used a custom integration.

I started with ha-reolink-talk, Matthew Holm’s integration based on joeblack2k’s Reolink Talk. It already provides camera media players for audio files and text-to-speech, including cameras behind an NVR. I had Codex investigate and fix a couple of problems with my setup in a fork.

The first was camera discovery. Codex found that a separate request to the NVR failed because of its self-signed HTTPS certificate. The error was caught, and discovery quietly fell back to channel 0. I’m still wondering how the hell this worked in the upstream author’s setup without hitting that certificate error. Meanwhile, Home Assistant’s official Reolink integration was already connected and knew which channels existed. Codex changed discovery to reuse that information, which got the right cameras to show up.

The audio framing needed fixing too. The encoded audio blocks weren’t being packaged the way the camera expected. Codex corrected the block metadata, padding, and stream framing, which got speech playing through the camera. Those fixes are in my fork and an upstream pull request.

Once playback worked, I had Codex implement the laundry automations. Each appliance’s LocalThings progress sensor reaching finish triggers an announcement through the camera by the stairs. Piper supplies the speech: “The washer has finished its cycle,” or the dryer equivalent.

I had Codex put together a few versions of the sound for me to try, including repeated flute announcements and a Samsung completion melody. With the appliance and the camera playing the Samsung melody out of sync, it was a confusing cacophony. I went with one short tone followed by speech instead. The washer gets a higher tone and the dryer a lower one. Codex rendered each tone and its spoken message together into one audio file, so the camera only has to start playback once.

The laundry automations are specific to this house, but the integration fixes are there for other Reolink owners to use. A camera speaker is a pretty convenient little announcement speaker when it’s already in the right place.

12:00 am / Home assistant , Reolink , Automation , Ai assisted , Codex