u/ASTRAL_EMBERS

Koensayr: Bluetooth metadata on the Y1 (and other stuff!)

Disclaimer: This is a firmware patcher. It modifies the /system partition on your Y1. I take no responsibility if you brick the device. Only try this if you understand what the patches are doing and how to recover if something goes sideways (worst case: re-flash a known-good stock rom.zip via MTKClient and you're back to square one). If you can't follow the README's setup steps or read the patch source, please don't run it.

A bit of background for anyone new to this project: I started Koensayr as a way to teach myself agentic AI workflows on something genuinely hard, rather than continuing to ignore the whole AI space. "Fix the Artists --> Albums workflow on a $60 MP3 player" was the original scope. A few weeks and an embarrassingly deep dive into the MediaTek AVRCP stack later, here we are.

For reference, my two previous posts are here and here.

Back on May 7 I posted that AVRCP metadata over Bluetooth was finally showing up on the Y1, but warned it was still WIP status and not spec-compliant. That's no longer the case.. It's ready for wider testing. I still expect bugs, but I can't do a larger-scale test on devices that I don't own. I only tested this on a few endpoints that I had available (Kia EV6, Samsung The Frame Pro TV, Sonos Roam, and one still being debugged: Chevrolet Bolt EV) and suspect that other devices may or may not behave differently. Either way, more data points aren't a bad thing.

After patching with Koensayr, when you pair the Y1 to a car head unit / Bluetooth speaker / TV / anything else that implements the receiving end of AVRCP, here's what you get:

  • Track metadata on the peer screen - Title, Artist, Album, Genre, track number, total tracks, and duration all show up on the Now Playing screen of whatever you've paired to.
  • Live playhead - the progress bar on the car screen moves in real time.
  • Discrete PLAY / PAUSE / STOP / NEXT / PREV - clean PASSTHROUGH routing from steering-wheel buttons, including on head units that don't tolerate the "PLAY toggles to pause" behaviour some media targets do.
  • A2DP stream survives pauses - silence-timeout was tearing down the audio connection on the stock firmware, which broke some car setups that didn't auto-reconnect. Fixed that.
  • Strict-spec compatibility - infotainment systems that gate the Now Playing pane on the target advertising the right device class / event set / character-set capability should now pass those checks. Hardware-tested across a small set of receivers at this point and the AVRCP Target 1.3 feature compliance scorecard closes every Mandatory and every Optional row.

Plus, stock firmware 3.0.7 is supported as of today (alongside 3.0.2). The MediaTek Bluetooth binaries didn't change between the two releases (only the music APK did) so the smali patches now handle both versions automatically.

For my fellow nerds: The stock AVRCP target on this device is MediaTek's, hardcoded for AVRCP 1.0 passthrough (no metadata, no event subscriptions, nothing). Getting full 1.3 behaviour required injecting a trampoline chain into libextavrcp_jni.so that catches inbound AVRCP commands, builds spec-compliant 1.3 responses by calling the response-builder primitives already present in libextavrcp.so, and pushes them back to the AVCTP layer. A small Android service (Y1Bridge) and the patched music app feed live state through a watched-files protocol so the trampolines always have fresh metadata to emit. Full architecture diagram, byte-level patch reference, and the investigation trail are in the repo's docs/.

How to use:

GitHub: github.com/SeanathanVT/koensayr. Linux host required (mounts system.img via loopback and uses GNU sed, Mac/Windows users will need a Linux VM).

Roughly: one-time setup clones MTKClient and builds the two artifacts that --all needs.. src/su (the ~900-byte setuid su binary for --root, built with make) and src/Y1Bridge (the Android service that satisfies the MtkBt Binder contract for --avrcp, built with Gradle). Then drop your stock rom.zip (3.0.2 or 3.0.7) into staging/, run ./apply.bash --all, and the script handles the rest: pulling system.img out of the zip, loop-mounting it, applying every patch, unmounting, and flashing to the device via MTKClient. You will be prompted to plug in the Y1 and hit the boot button next to the USB-C port. You can also shut it down normally, keep it unplugged, and then just plug it in when it's ready to flash.

The README's Quick start section has the exact commands and toolchain prerequisites. Individual flags (--avrcp, --bluetooth, --music-apk, --adb, --remove-apps, --root) are all selectable on their own if you don't want the full bundle.

Caveats:

  • You need the right rom.zip MD5 from one of the supported builds (v3.0.2, v3.0.7). To prevent weirdness, unsupported versions will be rejected by the patcher.
  • Receivers that don't implement AVRCP correctly (some cheap BT speakers, some older car infotainment systems) may not properly render metadata regardless of what the Y1 sends. Most modern infotainment systems work; if you've got a strict one that doesn't, debug logs may need to be provided to troubleshoot and resolve (again, I can't reproduce every quirk locally as I don't have every possible Bluetooth endpoint available for testing purposes).

Let me know how it works for you, and if you encounter any weirdness with your own Bluetooth devices! If you find bugs and you know what you are doing and would like to implement any changes / fixes, feel free to fork, fix, and submit a Pull Request to that end! Or don't, that's the beauty of open source. :)

Also, thanks to those of you who've been DMing me about this project over the last few weeks. I hope it's interesting to you, and maybe something from this effort will get pulled into the Rockbox effort. I'd love to at least get a shoutout by the Rockbox team if anything from this project is used upstream (even the Bluetooth stack research stuff), but if not, it's cool. Side note, I've been messing with a more native port of Rockbox (Linux-based, not APK-based, bypassing Android entirely), but that's a post for another day.

Stay curious, y'all!

Edit 1: I should note that if your Y1 is already paired to something, you may need to clear / re-pair it before your endpoint "recognizes" the new feature set and allows metadata to flow.

Edit 2: Well, my SD card finally shat the bed. Can't even reformat it or wipe it with dd via ADB because I just get I/O errors. Tight.

u/ASTRAL_EMBERS — 5 days ago

(WIP) Bluetooth metadata (AVRCP v1.3) on the Y1

A couple weeks ago I posted about patching the stock music app to fix the Artist navigation. That was just the beginning, apparently. I've since fallen down the proverbial rabbit hole. A few of you have hit me up about it, so I wanted to give a bit of an update.

I (along with doing an embarrasingly deep dive into optimizing Claude Code's agentic workflows) have been building out and testing a firmware patcher for the Y1 called Koensayr. The name's a bit of a Star Wars deep cut (Koensayr Manufacturing made the Y-Wing starfighter.. Y-Wing.. Y1.. I thought it worked well for this effort). It handles the music APK patch from my last post, but also covers Bluetooth pairing fixes (makes it more reliable when pairing to car infotainment systems), ADB debugging, bloatware removal (there are a lot of APKs on here that have no business being on here..), and root via ADB (not app-level root, as I have no use for that, but you can elevate in an adb shell session via the su command.

The other thing I've been banging my head against: Artist / Track / Album metadata support when playing over Bluetooth. As of today, metadata for Title, Artist, and Album are reliably showing up simultaneously on most peer device's Now Playing screen (which should have just worked out of the box, but here we are). Video below.

The short version of how: the MediaTek AVRCP target implementation only handles AVRCP v1.0 passthrough (play / pause / skip) out of the box, and basically nothing else. Getting metadata out required patching a chain of binary "trampolines" into libextavrcp_jni.so to intercept inbound AVRCP commands and synthesize proper AVRCP v1.3 responses, bypassing the incomplete MediaTek Java layer entirely. Perfectly normal stuff for a media player you just want to work in your car.

I'm still working to get it fully AVRCP v1.3 spec-compliant (so that theoretically there should be no edge cases as long as the target device properly implements the AVRCP spec) before a proper release, but it's relatively close. Stay tuned, nerds.

u/ASTRAL_EMBERS — 15 days ago

Hey all! A few weeks back I stumbled upon u/CouchPotatophile's post in this sub about an app that he built to interact with his personal WalkingPad (original thread here). I ran into a couple issues with it running on my MacBook, so I posted a Pull Request with the fix on the original GitHub project. It sat unanswered for a while, and I was unsure if he was still actively maintaining it (since he did say it was intended for personal use in the original post).

I just saw today that he recommended that people fork it and iterate on it if they want any changes. As a challenge to myself to start learning how to work with locally-deployed agentic AI, I ended up forking it and starting my own version last week and have been iterating on it under a new name: WalkingDad.

WalkingDad: Your WalkingPad's original app is trash. This isn't.

For my fellow nerds out there, I used this as opportunity to teach myself how to run LLMs locally as a backend for local agentic coding agents. In my case, this is currently llama.cpp + unsloth/Qwen3.6-27B-GGUF + the VS Code Cline extension (paired with a local container running Cline CLI) all running on a beefy MacBook Pro. I've learned a shitload in working with this setup.

Anyway, here's a high level overview of what changed since the fork:

v1.0.0: Bluetooth LE reliability fixes (the original PR) and Dark Mode support

  • Fixed cross-platform BLE connection issues: context manager scanning, exponential backoff retry, event loop cleanup, and Bleak API version fallbacks across macOS, Windows, and Linux
  • 3-state dark mode (Light / Dark / System)

v1.1.0: Graceful shutdown overhaul

  • Stopped the belt on exit in all cases: normal shutdown, Ctrl+C, or killed process
  • Signal handlers (SIGTERM/SIGINT) trigger cleanup sequence: stop belt --> standby mode --> BLE disconnect --> exit
  • Process-isolated Waitress subprocess so Ctrl+C hits the wrapper, not the server
  • Web UI now shows a shutdown message instead of freezing

v1.2.0: Session history log (this one is probably the coolest)

  • Completed sessions save to session_history.json with date, duration, distance (km/mi), steps, calories, and average speed
  • Start screen shows the last 10 sessions in a table (love this)
  • "End Session" button explicitly saves stats and resets counters
  • CSV export (/export_csv) for external tracking
  • In-progress sessions are captured on shutdown

A more detailed CHANGELOG.md is available on the project page (here).

No setup beyond the Python virtual environment pip install -r requirements.txt and opening your browser. Also, if you have a WalkingPad other than the King Smith WalkingPad C2, you will have to update the Bluetooth name (on this line) before starting the app. I don't know what the default value is for this on other WalkingPad models, so for now that's a manual change.

Repo link above if anyone wants to try it or suggest features. No promises on whether I will implement them or not as this was also started for my own personal use, but I just might consider some of them if they are good. Let me know what you think! 🤘

reddit.com
u/ASTRAL_EMBERS — 16 days ago

One of my biggest frustrations with this device (aside from the broken AVRCP) is the Artist navigation. Instead of showing a list of Albums by the selected Artist, the stock Y1 app jumps straight to a flat list of every song by that artist. Since the Y1 devs don't seem interested in changing this, and I have no desire to switch to Rockbox, I decided to fix it myself (begrudgingly with some help from Claude).

I've been having a bit of an existential crisis regarding the role of AI in the tech world. Instead of continuing to ignore it, I used this as a project to work alongside it. I used Claude as a "pair programmer" to help with the reverse engineering and Smali logic, forcing myself to use the AI in an agentic manner rather than just letting it do the work like some vibe coder clown.

The goal was to intercept the navigation path so that Home --> Music --> Artists --> [Artist] triggers the existing Albums UI (for only the selected artist) instead of the song list.

Path Before After
Home → Music → Artists → [Artist] Flat song list for that artist Albums by that artist (same UI as Albums screen)
Home → Music → Artists → [Artist] → [Album] N/A Songs in that specific album, sorted by track number
Home → Music → Albums All albums, unchanged All albums, unchanged

For my fellow nerds, the end result was a Python script that patches a version-agnostic Y1 app .apk file to support this feature. There are 3 patches.. Two Smali (compiled Java code, since we don't have the source for this thing) byte code patches to existing methods in the decompiled .apk (ArtistsActivity.confirm() and AlbumsActivity.initView()), and then one patch that changes the scope of Y1Repository.songDao() from a private method to a public method so that we can read from it elsewhere. I've only tested it on 3.0.2 and 3.0.7, but it theoretically works on any version. Patch the .apk, inject it into system.img with MTKClient, and reboot.

Wish this was the stock behavior, but alas. Anyway, here's what it looks like. Innioasis devs, please implement this navigation path.

u/ASTRAL_EMBERS — 26 days ago