Commissioning a 25 A MQTT contactor: from unboxing to Home Assistant to long-term maintenance
A 10 A smart plug is easy to reason about. This article is about the other end of the range: a 25 A, 6000 W contactor — the kind of circuit that runs an immersion heater, a workshop compressor, or an EV charger. Getting it wrong is not "the lamp stays on", it's a trip and a phone call to an electrician.
I'll cover commissioning it onto your own broker, wiring it into Home Assistant, and the maintenance commands that matter once it's been in a cabinet for a year.
The device is the GemeOpen GSCW1M2P (Smart Contactor 25A-S2 Plus-WiFi). Disclosure up front: I work on these. The config below is what I run on my own bench, and the caveats are the ones our support threads actually generate.
What this device is, and what it isn't
| Spec | Value |
|---|---|
| Model | GSCW1M2P |
| Rated current | 25 A working |
| Max load | 6000 W resistive |
| Input voltage | 110–260 V AC, wide range |
| Channels | 1 |
| Standby draw | under 4 W |
| Comms | 2.4 GHz WiFi, ESP8266 |
| Protocols | MQTT, TCP, HTTP |
| Housing | 50 × 110 × 32 mm, V0 flame-retardant PC/ABS |
| Rated life | 5,000,000 mechanical / 50,000 electrical operations |
What it is: a contactor you add to an existing distribution board, downstream of your normal overcurrent protection, so a high-power circuit becomes MQTT-controllable without replacing the consumer unit. That's the install pattern in the vendor's own documentation — added into the box, existing breaker stays.
What it is not: a breaker. It has no overcurrent or earth-leakage protection of its own. It switches a circuit; it does not protect one. Keep your MCB/RCBO upstream, and have a qualified electrician do the wiring. I'm not going to pretend this is a plug-in accessory — at 25 A the installation is the project.
Also note the load rating is specified for resistive loads. Motors and transformers have inrush and a worse power factor, so derate accordingly and check the manual before you put a compressor on it.
Commissioning
Step 1 — Get it on the network
Same provisioning flow as the rest of the range: power it up, hold the button about 6 seconds until the LED blinks, join the GemeOpen-xxxx hotspot, and set your 2.4 GHz SSID on the captive page (192.168.4.1). No app account.
2.4 GHz only. A 5 GHz-only network or an IoT VLAN with client isolation will break this.
Step 2 — Point it at your broker
Out of the box it talks to the vendor's test broker. You redirect it with one message, sent through that broker:
{
"type": "custom",
"protocol": "mqtt",
"server": "192.168.1.10",
"port": "1883",
"clientId": "ca2b96693fe2",
"username": "ha",
"password": "yourpassword",
"publish": "/workshop/cw1/report",
"subcribe": "/workshop/cw1/cmd"
}
Then power cycle it. Custom MQTT only takes effect after a restart — saving and walking away does nothing. This is the number one support question about this device, and I have hit it myself at 11pm, so: it's real.
Field-name trap, and it matters more on this device. The examples in this product's own documentation use subcribe — missing the "s" — because the documented firmware is 2.2.0. On firmware 2.3.8 and later the field is spelled subscribe. If your messages vanish into the void, that's the first thing to check. Ask the device what it's running:
{ "type": "info" }
The reply includes version and type. Note the version — you'll want it later.
Step 3 — The setting that actually matters for a contactor
Before you wire anything to it, set the power-on default state:
{ "onState": 1, "type": "setting" }
Three values:
-
0— memory: restores the state from before the power cut -
1— off: comes back up de-energised -
2— on: comes back up energised
For a 6 kW circuit, set onState: 1. A power cut at 14:00 should not mean your water heater or your compressor restarts at 14:02 while nobody is home. The docs flag onState: 2 as a safety risk for exactly this reason, and they're right. If you specifically want memory behaviour for, say, a lighting circuit, 0 is there — but not for a high-power load.
Step 4 — Lock it down
Two locks, and for a device sitting in a shared electrical closet both are worth turning on:
{ "keyLock": 1, "messageId": "1", "type": "setting" }
{ "type": "setting", "wifiLock": 1 }
keyLock disables the physical button — the contactor can then only be driven over MQTT/TCP. wifiLock disables long-press re-provisioning, so nobody can walk up to the cabinet and re-point the device at their own network. Both are reversible over the protocol, which is the point: you keep control, they don't get a button.
The command surface
Everything is plain JSON on two topics. Here's the whole thing:
| Command | Payload you send | What comes back |
|---|---|---|
| Switch on/off | {"key":1,"type":"event"} |
Full state snapshot, no messageId echo
|
| Set report interval | {"timerEnable":1,"timerInterval":60,"type":"setting"} |
Echoes the two fields |
| Read live stats | {"type":"statistic"} |
current, voltage, power, energy, key, mac
|
| Read device info | {"type":"info"} |
IP, MAC, firmware, SSID, signal, locks, timer config |
| Read protocol config | {"type":"protocol"} |
server, port, clientId, topics |
| Power-on default | {"onState":1,"type":"setting"} |
Full state snapshot |
| Key lock | {"keyLock":1,"messageId":"1","type":"setting"} |
Full state snapshot + messageId
|
| WiFi lock | {"type":"setting","wifiLock":1} |
Full state snapshot |
| Custom MQTT | {"type":"custom",...} |
Echoes the config |
| Custom TCP | {"type":"custom","protocol":"tcp",...} |
Echoes the config |
| Clear energy | {"system":"energy-clear","type":"setting"} |
{"commandName":"energy-clear","success":true,...} |
| Restart | {"system":"restart","type":"setting"} |
Echoes |
| Factory reset | {"type":"setting","system":"reset"} |
Echoes |
Two parser gotchas that will bite you:
-
The telemetry payload has no
commandName. The periodic report is just{"current":15.027,"energy":25.047,"key":0,"mac":"ca2b96693fe2","messageId":"timing","power":2921.511,"voltage":226.024}— no command name, andmessageIdis the literal string"timing", not an echo of anything you sent. If your code dispatches oncommandName, it will silently drop every telemetry message. -
messageIdcorrelation is inconsistent. Command acks for switching and settings return a full state snapshot without echoing yourmessageId;energy-cleardoes echo it. Treat the report topic as a state stream, not as an RPC response channel.
First contact:
mosquitto_sub -h 192.168.1.10 -t '/workshop/cw1/report' -v
mosquitto_pub -h 192.168.1.10 -t '/workshop/cw1/cmd' \
-m '{"key":1,"type":"event"}'
One message, relay pulls in. That's the entire control protocol.
Application: an off-peak water heater, done properly
This is the use case I'd actually recommend for a 25 A contactor, because it's the one where "the cloud service shut down" stops being an inconvenience.
Circuit: a 3 kW immersion heater on its own RCBO, contactor in series, downstream. Set the reporting interval to something sane — 60 seconds is plenty and keeps the recorder database small:
{ "timerEnable": 1, "timerInterval": 60, "type": "setting" }
Range is 5 to 86400 seconds. Home Assistant config — one switch, four sensors:
mqtt:
switch:
- name: "Immersion Heater"
unique_id: gemeopen_immersion
state_topic: "/workshop/cw1/report"
command_topic: "/workshop/cw1/cmd"
payload_on: '{"key":1,"type":"event"}'
payload_off: '{"key":0,"type":"event"}'
state_on: 1
state_off: 0
value_template: "{{ value_json.key }}"
optimistic: false
qos: 1
sensor:
- name: "Immersion Heater Power"
unique_id: gemeopen_immersion_power
state_topic: "/workshop/cw1/report"
value_template: "{{ value_json.power if value_json.power is defined else this.state }}"
unit_of_measurement: "W"
device_class: power
state_class: measurement
- name: "Immersion Heater Current"
unique_id: gemeopen_immersion_current
state_topic: "/workshop/cw1/report"
value_template: "{{ value_json.current if value_json.current is defined else this.state }}"
unit_of_measurement: "A"
device_class: current
state_class: measurement
- name: "Immersion Heater Energy"
unique_id: gemeopen_immersion_energy
state_topic: "/workshop/cw1/report"
value_template: "{{ value_json.energy if value_json.energy is defined else this.state }}"
unit_of_measurement: "kWh"
device_class: energy
state_class: total_increasing
The is defined guard on every sensor is not optional here. The report topic carries both the switch acks (which have key but no electrical data) and the telemetry (which has electrical data but no commandName). Without the guard, HA logs a template warning on every toggle.
Now the automations. Off-peak window with a manual enable, plus a current guard:
automation:
- alias: "Immersion heater on off-peak"
trigger:
- platform: time
at: "00:30:00"
condition:
- condition: state
entity_id: input_boolean.immersion_enabled
state: "on"
action:
- service: switch.turn_on
target:
entity_id: switch.immersion_heater
- alias: "Immersion heater off at peak"
trigger:
- platform: time
at: "07:00:00"
action:
- service: switch.turn_off
target:
entity_id: switch.immersion_heater
- alias: "Contactor current guard"
trigger:
- platform: numeric_state
entity_id: sensor.immersion_heater_current
above: 20
for: "00:00:30"
action:
- service: switch.turn_off
target:
entity_id: switch.immersion_heater
- service: notify.persistent_notification.create
data:
message: "Immersion circuit over 20 A for 30 s - contactor opened"
That last automation is a convenience guard, not a protection function. It reacts in seconds; an MCB reacts in milliseconds. If your upstream protection is correctly rated, it should never fire. If it fires regularly, you have a wiring or load problem, not an automation problem.
Cumulative energy survives power loss — only an explicit clear resets it. Feed sensor.immersion_heater_energy into the Energy Dashboard and you get per-circuit cost tracking with nothing leaving the house.
Maintenance
The commands you'll actually use once it's been in a cabinet for a while.
Restart after any config change. Changing broker, topics, static IP or WiFi credentials requires a restart:
{ "system": "restart", "type": "setting" }
The device reconnects to the network and your saved config is retained. The IP address may change — so put a DHCP reservation on it keyed to the MAC, or you'll be re-reading info-all every time it moves.
Clear the energy counter. Irreversible, and it only clears the counter:
{ "system": "energy-clear", "type": "setting" }
Snapshot your long-term data before you run this if you care about history.
Factory reset — the one that costs you a site visit:
{ "type": "setting", "system": "reset" }
This wipes WiFi credentials and custom MQTT. The device goes back to the vendor's test broker, which means the only ways back are: go through the vendor broker with a fresh custom message, or physically long-press it back into provisioning mode. If the device is inside a closed cabinet, factory reset is a ladder-and-screwdriver job. Don't run it casually from an automation.
Do the lifetime math before you deploy. 50,000 electrical operations is the rated figure. At 4 cycles a day that's roughly 34 years. At 100 cycles a day — a compressor cycling on pressure — it's about 16 months. If your application switches frequently, either pick a different device class or plan the replacement.
Budget the standby power. Under 4 W, always on. That's about 35 kWh a year — roughly £10 on a UK tariff, so factor it in when the circuit it controls only runs a few hours a day. For a water heater on a timer it pays for itself immediately; for something you switch twice a week it might not.
Check the firmware occasionally with {"type":"info"} and note the version. Two things have changed across versions that affect you: the subcribe/subscribe field spelling at 2.3.8, and TLS support (see below).
Thermals. 6000 W resistive in a 50 × 110 × 32 mm V0 housing is fine for the device, but the cabinet is your problem. If it's going in a sealed enclosure with other heat-generating gear, derate the load or add ventilation. A contactor that cooks itself is a fire risk, not a bug report.
Honest limitations
-
No MQTT over TLS yet.
mqttssupport is in development. Inside a trusted LAN, fine. Crossing the internet, tunnel it or use a VPN — and don't put credentials in a plaintext topic on a public broker. - No MQTT Discovery. Manual YAML. Tedious, but nothing gets renamed out from under you.
- No gateway needed, and no app account. If you want one, this is the wrong product.
- Won't join Mi Home, HiLink or Tuya — that's a design choice, not a gap. It targets your own broker and platforms like Home Assistant, ThingsBoard, or the major cloud IoT services.
- The vendor console is a test tool, not a production cloud — their own docs say so. Don't build a commercial product on it. Run your own broker.
- 6000 W is the resistive rating. Motors, transformers and anything with inrush need derating.
Where this fits
If you already run a broker, this is a boring, reliable component: it turns one high-power circuit into a JSON-controllable switch with metering attached, and the data goes to your disk and nowhere else. Commissioning takes about 40 minutes, most of which is the reboot requirement.
If you were hoping for a consumer product with an app, buy something else.
Full command reference, published openly with no sign-up:
https://www.smart-bird.cn/doc/product/GSCW/device/GSCW1M2P/command
Device page: https://www.smart-bird.cn/product/info/GSCW1M2P.html
Questions welcome in the comments — especially if you're running this on EMQX with ACLs, or driving it from Node-RED instead of Home Assistant.
Disclosure: written by the GemeOpen team — we build these devices. Every gotcha above comes from real support threads and my own bench, not a brainstorm. If something behaves differently on your unit, tell me and I'll find out why.
Top comments (0)