kamoer reef master spa + ha integration

  • Thread starter Thread starter ramarro
  • Start date Start date
  • Tagged users Tagged users None

ramarro

New Member
View Badges
Joined
May 18, 2025
Messages
16
Reaction score
0
Location
italy
Rating - 0%
0   0   0
Hi,

i am not sure if this will be an useful topic, and could be quite "long" and "complex" so let me ask if someone is intrested or not before i start.

i have a reef master spa from kamoer, a device that test 6 params.

what i did to make it more "cool" (from an engineer point of view lol) is to integrate it with home assistant.


the topic will cover this part
1. setup an esp32 ov5640 auto focus camera
2. provide you an stl that you can print to attach the esp32 board, including a shader to avoid glare and reflection on lcd display
3. a python program that get the image from esp32, extract the 6 values, do some basic validation of those values, and send them to your home assistant.


at this point the possibilities are quite a lot...
what i personal did is
A. kh + ca + mg is now controlled by dosing pump, that run 1h after every test, auto-adjusting the level of the liquid before dosing (yes, i can provide the yml to get basic idea on it)
B. dosing vodka if no3 is above a certain value
C. dosing lantanum chloride if po4 is above certain level

to make sure don't make the tank explode, A+B+C have some sanity check to understand if the value of the sensor is updated (ie: kamoer is working, extracting software is working, camera software is working)

extracting software use easyocr lib, that's supposed to use AI (buzzword used!!) and is quite effective on extracting value, it also do some little trick including post processing image to better extract number. Also it cross validate number on a certain range, to make sure reef master doesn't get nuts. And it can run on docker.

So again, it will take a bit of time to go thru all this topic, there are some 'tech' things and i am not sure who is intrested on a topic that's more on the software development side rather than on reefing, but if anyone is itnrested just write here and i'll post all the info i have, including the code & stl
 
Great timing. Just started researching this machine. You are taking it steps far beyond what i have imagined but following along.

I am guessing you like it and happy with accuracy?
 
for my need is enough, i am using the new reagent (2 comes with bags not bottle to avoid air contact i think), the only drawback is that you have to hang them upside down, but for me wasn't that bad.

i always used salifert test, and i can tell they are close enough (plus you can calibrate the reefmaster to match your test if that's important to you). for now doing 4x test on kh and 2x ca/mg i never saw them goes nuts (i am testing that much just because i was finishing the integration with home assistant, i dobut it's needed). I also set 3x rinse time, as for me time of test and water waste isn't that relevant. The default is 2x. in 2 days doing so many test get a waste of 300ml more or less if i remember correctly (but i flush direclty in the drain so this data was on when i was at real first time testing days)

one note: the reagent QR code that was shipped was 'too new' and not yet detected by device. Support took 12hrs to fix it and was saturday, not bad imho... they also say that everytime you change the reagent, it's a good practice with a siringe to wash the tube. Also they mention on ticket that the "cleanest" inlet you use for water (probably mean without derbies) the less likely you are gonna get issue... seems reasonable :)
 
for my need is enough, i am using the new reagent (2 comes with bags not bottle to avoid air contact i think), the only drawback is that you have to hang them upside down, but for me wasn't that bad.

i always used salifert test, and i can tell they are close enough (plus you can calibrate the reefmaster to match your test if that's important to you). for now doing 4x test on kh and 2x ca/mg i never saw them goes nuts (i am testing that much just because i was finishing the integration with home assistant, i dobut it's needed). I also set 3x rinse time, as for me time of test and water waste isn't that relevant. The default is 2x. in 2 days doing so many test get a waste of 300ml more or less if i remember correctly (but i flush direclty in the drain so this data was on when i was at real first time testing days)

one note: the reagent QR code that was shipped was 'too new' and not yet detected by device. Support took 12hrs to fix it and was saturday, not bad imho... they also say that everytime you change the reagent, it's a good practice with a siringe to wash the tube. Also they mention on ticket that the "cleanest" inlet you use for water (probably mean without derbies) the less likely you are gonna get issue... seems reasonable :)
Hello,

Nothing to ask about your topic but it seems you know a bit about this equipment. I recently got mine and I see multi issues with the equipment, The biggest is the accuracy, I have tested CA/MG 4 times and it won't take a measument. Keeps saying CA out of range 60. No clue what that means. Hanna Checker says my CA is 459 and MG is1182. Looking at the test range in the kamoer app it is within range. KH is measuring 3 dkh lower than Hanna, I'm thinking calibration issue but it should be calibrated at the factory. ideas???
 
Did you try to open a ticket in the app? They was quite helpful to me.

For me the cause of initial issue was the filter for saltwater inlet. I had to clean it with a little syringe. I also had to fill saltwater 1 e 2 few times before it was actually filled.

Also it worth nothing but I guess you added all the powder to reagents? Ca/mg have a powder in reagent2 if my memory don't fail me

Anyway your best shot is to raise issue on the app.

As for difference with Hanna, what if Hanna is wrong? To proper do a compare/calibration you need a reference solution, like tropic marine multi reference, then you can compare and calibrate them.
Another option is to check with multiple test of multiple brand and try to find an average, but your best shot is to use the reference solution. Probably better to try to fix ca and MG reads before, as the inlet is shared for this tests (that's why it's what I am suspecting giving you issue)


Good luck👍
 
Did you try to open a ticket in the app? They was quite helpful to me.

For me the cause of initial issue was the filter for saltwater inlet. I had to clean it with a little syringe. I also had to fill saltwater 1 e 2 few times before it was actually filled.

Also it worth nothing but I guess you added all the powder to reagents? Ca/mg have a powder in reagent2 if my memory don't fail me

Anyway your best shot is to raise issue on the app.

As for difference with Hanna, what if Hanna is wrong? To proper do a compare/calibration you need a reference solution, like tropic marine multi reference, then you can compare and calibrate them.
Another option is to check with multiple test of multiple brand and try to find an average, but your best shot is to use the reference solution. Probably better to try to fix ca and MG reads before, as the inlet is shared for this tests (that's why it's what I am suspecting giving you issue)


Good luck👍
 
I’ve decrypted all Kamoer traffic.

Full local control achievable via Home Assistant and MQTT.

Kamoer use MQTT over TLS but the devices accept a self signed certificate 😂

This is why Chinese crap stays on an IoT VLAN!

If anyone is interested, I can provide a Claude prompt that will code an integration for you. Tried attaching it but R2R keeps blocking the attachment as its code. PM me for info 👍
 
I’ll upload the code as an attachment - I think that bypasses R2R restrictions.

I’m currently smashing Fable5 and am at 87% weekly usage despite paying for more tokens. 😬

Proof of life attached!

Need to finish parallel projects before I get limited.

Screenshot 2026-07-06 at 8.53.16 pm.png
 
Pasting the following into Claude should get you going 👍

# Local Home Assistant control of the Kamoer X2SR (no cloud, no app) — full teardown + how to replicate

**TL;DR:** The Kamoer X2SR auto water changer has no official Home Assistant
integration and no local API. I intercepted its network traffic, decoded the
protocol, and now drive it **entirely from my local Mosquitto + Home Assistant**
— start/stop the fill and drain heads with a chosen volume, and read the pump's
telemetry back — with the Kamoer cloud completely out of the loop. Here's exactly
how it works and how you can replicate it on your own unit.

⚠️ This is DIY reverse-engineering of your own hardware. It takes the pump off
Kamoer's cloud (the phone app will show it "offline"). It's reversible. Don't do
this on anything you can't afford to fiddle with, and be careful actuating a pump
that moves saltwater into your system.

---

## What the X2SR actually is, under the hood

- It's an **ESP8266** running **Alibaba Cloud IoT (Link Kit)**. It talks
**MQTT wrapped in TLS on port 1883** to an Alibaba broker.
- Two findings made this whole thing possible:
1. **It does not validate the server's TLS certificate.** It will happily
complete a TLS handshake against a self-signed cert. That means you can
stand in the middle of its "encrypted" connection.
2. **Its entire control protocol is tunnelled through a single text property
called `PumpSerialNumber`** — a hex string — inside otherwise-standard
Alibaba MQTT messages.

### The MQTT topics (Alibaba thing-model)
- Commands go to: `/sys/<ProductKey>/<DeviceName>/thing/service/property/set`
- The device posts telemetry to: `/sys/<ProductKey>/<DeviceName>/thing/event/property/post`
- Payload shape:
`{"method":"thing.service.property.set","id":"<any>","params":{"PumpSerialNumber":"<HEX>"},"version":"1.0.0"}`

For the X2SR the **ProductKey is `a1UbfLPJj7A`** (this appears to be the same for
all X2SR units — it identifies the product). The **DeviceName** and the MQTT
**password** are unique to your unit; you capture them (below).

### The decoded control frame (the hex inside `PumpSerialNumber`)
```
[seq] 02 00 07 00 08 00 0e [CH] [ACT] 00 [volume: float32, big-endian]
seq = a message-counter byte. The device just echoes it — any value works.
CH = 01 -> Add Sea Water (fill head) | 00 -> Discharge (drain head)
ACT = 01 -> START | 00 -> STOP
volume = target volume in mL as an IEEE-754 float32.
1000 mL = 447a0000, 500 mL = 43fa0000, 50 mL = 42480000
```
Example — **start the fill head for 1000 mL**: `A00200070008000e010100447a0000`

There's also a **read-only status poll** that makes the pump dump its telemetry
(running state + live float32 volume counters): `[seq]00000000080005`

---

## The architecture

```
X2SR (ESP8266) Your router / a small proxy Your Mosquitto + Home Assistant
│ MQTT-over-TLS :1883 │ │
│ │ TLS-terminating "injecting proxy": │
└── redirected (DNS or DNAT) ─────►│ • presents a self-signed RSA cert │
│ • relays plaintext MQTT to your broker ──┼─► logs in as the device
│ • **injects a SUBSCRIBE** for the │
│ command topic right after CONNECT │
│ • hides the resulting SUBACK │
└────────────────────────────────────────────┘
```

Two non-obvious things you MUST handle, or it won't work:

1. **The proxy needs an RSA self-signed cert, and TLS 1.2 with a legacy RSA
cipher.** The ESP8266 offers old ciphers (e.g. `AES256-SHA256`). If your
broker/proxy only presents a modern ECDSA cert (e.g. a Let's Encrypt cert) the
handshake dies with "no shared cipher." (In Python's `ssl`, set
`ctx.set_ciphers("ALL:@SECLEVEL=0")`.)

2. **The device never sends its own MQTT SUBSCRIBE.** Alibaba's platform uses
*server-side* subscriptions, so a normal broker has nothing to route commands
to — you can publish all day and the pump ignores it. The fix: the proxy
**injects a `SUBSCRIBE /sys/<pk>/<dn>/thing/service/#` on the device's session
right after it connects**, and swallows the SUBACK so the device's SDK isn't
confused. After that, commands you publish are delivered to the pump.

---

## Step-by-step to replicate

You need: your reef LAN, an MQTT broker (the HA Mosquitto add-on is fine), and a
way to (a) run a small proxy and (b) redirect the pump's traffic to it.

**1. Find the pump on your network.** Look for an Espressif MAC. Confirm it holds
a connection to an Alibaba IP (the `47.236.x.x` range) on port 1883.

**2. Capture the plaintext protocol (one-time).** Point the pump's broker
connection at a self-signed TLS listener and log what flows. `socat` is perfect:
```
socat -x -v OPENSSL-LISTEN:8883,cert=fake.crt,key=fake.key,verify=0,reuseaddr,fork \
OPENSSL:<real-alibaba-ip>:1883,verify=0
```
(Generate `fake.crt/fake.key` with `openssl req -x509 -newkey rsa:2048 ...`.)
Redirect the pump to this listener (see step 5), then open the Kamoer app and
press some buttons. In the capture you'll see, in the clear:
- the device's **CONNECT** → your **DeviceName**, MQTT **username**
(`<DeviceName>&<ProductKey>`) and **password**;
- the **commands** as `PumpSerialNumber` hex — decode them with the frame above.

**3. Let the device log in to YOUR broker.** Add the device's captured
username/password as a Mosquitto login (HA Mosquitto add-on → `logins`). The
password is a fixed-timestamp HMAC, so it's stable.

**4. Run the injecting proxy.** A ~120-line Python asyncio script: TLS listen
(RSA cert), relay to your broker, inject the SUBSCRIBE after CONNACK, swallow the
SUBACK. (Full script in the repo — `kamoer_proxy.py`.)

**5. Redirect the pump to the proxy.** Two options:
- **DNS override (most portable):** if the pump resolves its broker by
hostname (Alibaba Link Kit typically uses
`<ProductKey>.iot-as-mqtt.<region>.aliyuncs.com`), add a local DNS rewrite
(Pi-hole / AdGuard / your router's DNS) pointing that name at the proxy host.
No router shell needed.
- **Router DNAT (what I used):** on the router, redirect the pump's outbound
`:1883` to the proxy: `iptables -t nat -I PREROUTING -s <pump-ip> -p tcp
--dport 1883 -j DNAT --to-destination <proxy-ip>:8883`. If the proxy is on a
different host than the router, add a matching `MASQUERADE` (hairpin NAT).

**6. Control it from Home Assistant.** Publish the command frames with
`mqtt.publish`. Encode the volume float with the template `pack()` function:
`{{ pack(volume|float, '>f').hex() }}`. Subscribe to the `.../event/property/post`
topic for telemetry. (Full HA package in the repo — `kamoer.yaml`: an
`input_number` for volume, start/stop scripts for both heads, a status-poll
script, and a telemetry sensor.)

**7. Test safely.** First fire the **read-only status poll** — if the pump
answers with a telemetry blob, your command path works end-to-end. Then do a tiny
metered START (e.g. 50 mL) and an explicit STOP a few seconds later, watching the
pump. Only then trust bigger volumes.

---

## "Can this just be a Home Assistant integration?"

Mostly yes, with one caveat:
- The **proxy** can be shipped as a **Home Assistant add-on** (it's just a Python
container). Point the redirect at the HA host and you don't need a separate box.
- The **control entities** are already a HA package and could be a HACS custom
integration or blueprint.
- The **one irreducibly-external step is the redirect** (DNS rewrite or router
DNAT), because HA can't change how your pump routes its own traffic. That's a
one-time setup the user does on their router/DNS — everything else can be
packaged.

---

## Caveats / gotchas
- **The phone app will show the pump offline** once you redirect it — it's off
Kamoer's cloud. Fully reversible (drop the redirect and it goes back).
- **Make your proxy + redirect survive reboots** (systemd/init or a HA add-on),
and re-assert router DNAT if your firewall software rebuilds its rules.
- **Auto-stop behaviour:** I confirmed explicit START/STOP works reliably; I did
not exhaustively confirm whether the pump auto-stops at the target volume. Use
explicit STOP (or a timeout guard) in any unattended automation, and keep test
volumes tiny until you're sure.
- This is one person's teardown of one unit; firmware revisions may differ.

Happy to share the proxy script, the HA package, and my MQTT-decoder — ask and
I'll drop them here.

---

8% of my Fable5 usage remaining - I take no credit for any of this.
I spent a year trying to get this working.
Fable5 did it in under an hour!
 
Last edited:
I have HA setup to do a 45L water change each day at a set time. I can manually override and specify a manual input/output. The reason for this work is that I have two 1000L IBC's full of NSW. Every few months I misjudge topping them up and end up dumping 45L to waste whilst trying to fill from an empty tank. Yeah, I know the X2SR has level sensors but I'm not compromising the integrity of my containers when the plumbing runs though my roof-space! This way I can tell the X2SR to stop if my sump volume drops.

Screenshot 2026-07-06 at 10.17.54 pm.png


I did a big change today after dosing fluconazole. The pump heads can never be accurately calibrated against each other. This way it's set and forget.

I want to vacuum the crap out the tank? The sump volume drops, HA knows how much NSW and RODI to add to ensure my salinity is roughly maintained.
Screenshot 2026-07-06 at 10.04.01 pm.png



on that note, I have another post about a DIY conductivity probe for realtime salinity monitoring. Fable5 fixed my code there too and its way better than my apex and salinity guardian!
https://www.reef2reef.com/threads/realtime-salinity-monitoring.1138588/#post-14022257
Finally: my post on using a local LLM for tank analysis:
https://www.reef2reef.com/threads/d...cloud-no-subscriptions.1163414/#post-14421259
 
you could share your progress on github i think will be intresting for other diy ppl
 
https://github.com/ffirenmatana 😇

Most stuff is private but I'll work on making more of it public
you could share your progress on github i think will be intresting for other diy ppl


PSA: IF ANYONE WANTS TO DO AS I HAVE - NOTE THAT ITS NOT A ROBUST PRODUCT. DON'T TRY TO SEND SIMULTANEOUS COMMANDS, SUCH AS RUNNING BOTH HEADS AT THE SAME TIME. STAGGER COMMANDS LIKE YOU WOULD IN THE APP. A FEW SECONDS IS ALL THAT IS REQUIRED. WAIT FOR TELEMETRY AND THEN MOVE ON. IF YOU'RE HACKING THE FIRMWARE OF A DEVICE THATS ATTACHED TO YOUR TANK, BE SENSIBLE AND ATTACH AN UPSTREAM RELAY AS A FAILSAFE.
 
Last edited:
Back
Top
Home
Post thread…
Market
What's new