[External Converter]: [SBRC-005B-B / Shelly BLU Remote Control ZB] FW 1.2.13 sends manufacturer-specific commands – no `action` published
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 30/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- embedded-iot, javascript
- Domain
- embedded-iot
Research direction
The payload gives a full external converter with fzShellyOnOffRaw and fzShellyLevelRaw (raw-type fromZigbee handlers) and a parseRaw helper, plus the frame layout in the notes. Start by reading the built-in SBRC-005B-B definition, then check whether the manufacturer-specific frames from mfg code 0x1490 reach these handlers. Done means the reported on_N, brightness_step_*_N and action_channel payloads appear for all four channels, and the untested toggle and move variants are confirmed on real frames.
Written by the indexing model from the issue text.
Description
Link
https://www.shelly.com/products/shelly-blu-remote-control-zb-black
Database entry
{"id":40,"type":"EndDevice","ieeeAddr":"0xf84477fffe09a9e6","nwkAddr":61256,"manufId":5264,"manufName":"Shelly","powerSource":"Battery","modelId":"BLU Remote Control ZB","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":1,"inClusterList":[0,1,3,32],"outClusterList":[3,6,8],"clusters":{"genBasic":{"attributes":{"modelId":"BLU Remote Control ZB","manufacturerName":"Shelly","powerSource":3,"zclVersion":8,"appVersion":0,"stackVersion":0,"hwVersion":0,"dateCode":"20250505","swBuildId":"v1.2.13"}},"genPollCtrl":{"attributes":{"checkinInterval":14400}},"genPowerCfg":{"attributes":{"batteryPercentageRemaining":198,"batteryVoltage":30}}},"binds":[{"cluster":32,"type":"endpoint","deviceIeeeAddress":"0x00124b0031dea5a4","endpointID":1},{"cluster":0,"type":"endpoint","deviceIeeeAddress":"0x00124b0031dea5a4","endpointID":1},{"cluster":8,"type":"endpoint","deviceIeeeAddress":"0x00124b0031dea5a4","endpointID":1}],"configuredReportings":[],"meta":{}}},"appVersion":0,"stackVersion":0,"hwVersion":0,"dateCode":"20250505","swBuildId":"v1.2.13","zclVersion":8,"interviewCompleted":true,"interviewState":"SUCCESSFUL","meta":{},"lastSeen":1794251118958,"checkinInterval":3600}
Zigbee2MQTT version
2.14.3 (zigbee-herdsman 11.2.0, zigbee-herdsman-converters 26.125.0)
External converter
import * as m from 'zigbee-herdsman-converters/lib/modernExtend';
import * as exposes from 'zigbee-herdsman-converters/lib/exposes';
const e = exposes.presets;
const ea = exposes.access;
const SHELLY_MFG = 0x1490;
const lastSeq = new Map();
function isDuplicate(msg, seq) {
const key = msg.device.ieeeAddr;
if (lastSeq.get(key) === seq) return true;
lastSeq.set(key, seq);
return false;
}
function parseRaw(buf) {
const fc = buf[0];
let i = 1;
let mfg = null;
if (fc & 0x04) { mfg = buf.readUInt16LE(1); i = 3; }
return {fc, mfg, seq: buf[i], cmd: buf[i + 1], payload: buf.subarray(i + 2)};
}
const withChannel = (action, channel) => (channel ? `${action}_${channel}` : action);
const fzShellyOnOffRaw = {
cluster: 'genOnOff',
type: 'raw',
convert: (model, msg) => {
const {mfg, seq, cmd, payload} = parseRaw(msg.data);
if (mfg !== SHELLY_MFG || isDuplicate(msg, seq)) return;
const base = {0x80: 'off', 0x81: 'on', 0x82: 'toggle'}[cmd];
if (!base) return;
const channel = payload.length >= 1 ? payload[0] + 1 : undefined;
return {action: withChannel(base, channel), action_channel: channel};
},
};
const fzShellyLevelRaw = {
cluster: 'genLevelCtrl',
type: 'raw',
convert: (model, msg) => {
const {mfg, seq, cmd, payload} = parseRaw(msg.data);
if (mfg !== SHELLY_MFG || isDuplicate(msg, seq)) return;
const std = cmd & 0x7f;
const channel = payload.length ? payload[payload.length - 1] + 1 : undefined;
let r;
switch (std) {
case 0x00: case 0x04:
r = {action: 'brightness_move_to_level', action_level: payload[0], action_transition_time: payload.readUInt16LE(1) / 10}; break;
case 0x01: case 0x05:
r = {action: payload[0] === 0 ? 'brightness_move_up' : 'brightness_move_down', action_rate: payload[1]}; break;
case 0x02: case 0x06:
r = {action: payload[0] === 0 ? 'brightness_step_up' : 'brightness_step_down', action_step_size: payload[1], action_transition_time: payload.readUInt16LE(2) / 10}; break;
case 0x03: case 0x07:
r = {action: 'brightness_stop'}; break;
default: return;
}
r.action = withChannel(r.action, channel);
r.action_channel = channel;
return r;
},
};
const baseActions = ['on','off','toggle','brightness_step_up','brightness_step_down','brightness_move_up','brightness_move_down','brightness_stop','brightness_move_to_level'];
const actions = baseActions.flatMap((a) => [a, ...[1,2,3,4].map((ch) => `${a}_${ch}`)]);
export default {
zigbeeModel: ['BLU Remote Control ZB'],
fingerprint: [{modelID: 'BLU Remote Control ZB', manufacturerName: 'Shelly'}],
model: 'SBRC-005B-B',
vendor: 'Shelly',
description: 'BLU Remote Control ZB (FW 1.2.13+ manufacturer-specific commands)',
extend: [m.battery({percentageReporting: false}), m.identify(), m.commandsOnOff(), m.commandsLevelCtrl()],
fromZigbee: [fzShellyOnOffRaw, fzShellyLevelRaw],
exposes: [
e.action(actions),
e.numeric('action_channel', ea.STATE).withValueMin(1).withValueMax(4).withDescription('Active remote channel (1-4)'),
e.numeric('action_step_size', ea.STATE),
e.numeric('action_rate', ea.STATE),
e.numeric('action_level', ea.STATE),
e.numeric('action_transition_time', ea.STATE).withUnit('s'),
],
};
What does/doesn't work with the external definition?
Context: Shelly firmware v1.2.13 (2026-09-16, build 20260916-105646) changed the remote's Zigbee commands to manufacturer-specific (mfg code 0x1490). Shelly changelog: "SBRC Add custom ZB commands and custom attributes for group to channel mapping". The built-in SBRC-005B-B definition (standard commandsOnOff / commandsLevelCtrl) no longer matches anything — every button/wheel frame is logged as type 'raw' + No converter available, so no action is ever published. FW 0.0.1 / 1.2.12 still send standard commands and work with the built-in definition.
Frame format (reverse-engineered):
genOnOff: 05 90 14 <seq> 80|81|82 <channel>
genLevelCtrl: 05 90 14 <seq> <std_cmd | 0x80> <standard payload...> <channel>
05 = frame control: cluster-specific, manufacturer-specific, client→server
90 14 = manufacturer code 0x1490 (Shelly)
0x80 = off, 0x81 = on, 0x82 = toggle
0x86 = step_with_on_off (0x06 | 0x80): [mode, stepSize, transitionTime(LE16), channel]
channel = 0..3 → remote channel 1..4
Examples: [5,144,20,77,129,0] → on, ch1 · [5,144,20,81,134,1,90,3,0,0] → step down, size 90, tt 0.3 s, ch1 · [5,144,20,22,134,1,15,3,0,3] → step down, ch4. Every frame is sent twice with the same seq → dedup by seq.
Works with this converter:
- all 4 channels:
on_N,off_N,brightness_step_up_N,brightness_step_down_N(N = channel), plusaction_channel,action_step_size,action_transition_time - battery (via existing
genPowerCfgbind/reporting), identify, availability (genPollCtrlcheck-in)
Resulting payloads:
zigbee2mqtt/Pilot {"action":"on_4","action_channel":4,"linkquality":136}
zigbee2mqtt/Pilot {"action":"brightness_step_down_3","action_channel":3,"action_step_size":21,"action_transition_time":0.3,"linkquality":136}
Not tested: toggle (0x82), move/stop/move_to_level variants — decoded by analogy (std cmd | 0x80 + trailing channel byte), not observed on the wire.
Notes
fingerprintis needed in the external definition so it takes precedence over the built-inSBRC-005B-B; withzigbeeModelalone the built-in wins after restart.- The remote's binding table is very small: factory binds to groups 1–4 + Z2M's coordinator binds → the 4th coordinator bind fails with
TABLE_FULL. That's whym.battery({percentageReporting: false})— otherwiseconfigurefails on every reconnect. Consider making the built-in definition tolerant here. - The remote is a sleepy end device with a short poll window; binds/configure only succeed while the wheel is being rotated continuously (keeps it awake). Worth a note on the device page.
- After the FW update the IEEE address changed: FW 0.0.1 reported a placeholder IEEE (
0x694f69634762684a– same on several units, see #29104 / #30523), FW 1.2.13 reports a proper SiLabs EUI-64 (0xf84477fffe09a9e6). Users must re-pair after updating. - Z2M 2.14.3, Z-Stack 3.x.0 rev 20260310, HA OS add-on.
- Dominant language
- TypeScript
- Stars
- 15.8k
- Forks
- 2.1k
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 38
Getting set up
Starts the project's dev container in your browser, under your own GitHub account.
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Koenkk/zigbee2mqtt
-
[Feature request]: OTA for generated definitions of devices with an OTA client clusterPossibly taken @jabla claimed this 1 day ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 80/100
Koenkk/zigbee2mqtt#33684 · 1 comment ·
Maintainers usually reply within 1 day
-
new device support
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Koenkk/zigbee2mqtt#33674 · 1 comment ·
Maintainers usually reply within 1 day
-
[Feature request]: Support espzb adapter (ESP Zigbee SDK v2, ESP32-S31)Possibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Koenkk/zigbee2mqtt#33668 ·
Maintainers usually reply within 1 day
-
problem
Difficulty 1/5 Under an hour Newbie friendliness 76/100
Koenkk/zigbee2mqtt#33645 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
Koenkk/zigbee2mqtt#33613 · 2 comments ·
Maintainers usually reply within 1 day
All issues in Koenkk/zigbee2mqtt
Similar issues
-
area: web
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
inabagumi/shinju-date#6823 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
vercel-labs/ui#540 ·
Maintainers usually reply within 1 day
-
accepted ai-dev ai-implementing
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
livesport/homer#218 · 1 comment ·
Maintainers usually reply within 1 day
-
normalizePropName strips `#` characters from Figma property names instead of only the `#id` suffixOpen
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
figma/code-connect#419 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100