I recently managed to integrate my seven interconnected X-Sense SC07-W smoke/CO detectors into Home Assistant. This required a generic RTL-SDR and a few hours of reverse-engineering the RF protocol.
These sensors operate on the 915MHz band and use GFSK modulation. Below are the configurations to get these working as a binary sensor in HA.
1. rtl_433 Configuration
/config/rtl_433/xsense.conf.template:
Note on Matching: The match line below is “broad” to help you find your specific Network ID. Once you identify your 24-bit house code (the 6 hex digits following 5898), you can update the match to {104} and append your hex code to ignore neighboring systems.
# Disable all built-in decoders to save CPU
protocol -all
# Frequency and Sample Rate
# https://fcc.report/FCC-ID/2AU4DDBM/
frequency 915.275M
sample_rate 250k
# X-Sense SC07-W Protocol Decoder
# ---------------------------------------------------------
# Physical Layer: GFSK, 915.275 MHz, 10.0 kbps (100 us pulse width)
#
# Packet Anatomy (Total ~137 bits):
# [Preamble] 0x5555555555555554 - alternating-bit preamble + 0100 start frame delimiter
# [Bytes 0-1] Protocol header: 0x5898 - X-Sense Link+ manufacturer signature
# [Bytes 2-4] Network ID: 24-bit house code, programmed at pairing time
# [Byte 5] Flags. Bits 7-1 are constant (0xbe base). LSB: 0=normal, 1=button active
# [Byte 6] Message type:
# 0x00 (0) = Decode artifact - anomalous flag; discard
# 0x05 (5) = Standby - network resting state after a real smoke event clears
# 0x0d (13) = Standby - network resting state after a test button event clears
# 0x17 (23) = Alarm relay - peer sensor echoing an alarm trigger
# 0x19 (25) = Alarm relay - secondary relay variant
# 0x1f (31) = Alarm test source - test button long-press
# 0x23 (35) = Pair request - sensor requesting to join the network
# 0x25 (37) = Pair accept - network acknowledging a new sensor
# 0x82 (130) = Alarm smoke source - REAL smoke detection
# 0xc0 (192) = Button active - NOT mapped Safe; appears during active alarms
# 0x?? (??) = CO alarm - not yet captured; failsafe ON covers any unmapped type
# [Byte 7] CRC-8 over bytes 0-6: poly=0x07, init=0xb2
# [Tail] Padding bits
decoder {
name=XSense,
modulation=FSK_PCM,
short=100,
long=100,
reset=5000,
match={80}0x55555555555555545898,
get=net_id:@80:{24},
get=flag:@104:{8},
get=msg_type:@112:{8},
get=event:@112:{8}:[0:Decode_Error 5:Standby 13:Standby 23:Alarm_Relay 25:Alarm_Relay 31:Alarm_Test 35:Pair_Request 37:Pair_Accept 130:Alarm_Smoke 192:Button_Active],
get=alarm_logic:@112:{8}:[0:Safe 5:Safe 13:Safe 35:Safe 37:Safe]
}
output mqtt://${host}:${port},user=${username},pass=${password},retain=${retain}
report_meta time:iso:usec:tz
output kv
2. Home Assistant MQTT Configuration
Add the following to your mqtt.yaml (ensure you have mqtt: !include mqtt.yaml in your configuration.yaml).
This setup creates a single “Network” device in HA. It uses a Failsafe approach: any message type not explicitly mapped to “Safe” (like a real smoke event 130 or an uncaptured CO event) will trip the binary sensor to ON.
binary_sensor:
- name: "X-Sense House Alarm"
unique_id: "xsense_house_alarm_binary"
state_topic: "rtl_433/+/devices/XSense/rows/0/alarm_logic"
device_class: smoke
off_delay: 60
device: &xsense_dev
identifiers: ["xsense_network"]
name: "X-Sense Smoke Alarm Network"
manufacturer: "X-Sense"
model: "SC07-W / Link+"
value_template: >-
{% if value == 'Safe' %}
{# If HA just rebooted (unknown), use the standby to set it to OFF #}
{# Otherwise, IGNORE standby events so they don't overwrite an active alarm #}
{% if this is not defined or this.state in ['unknown', 'unavailable'] %}
OFF
{% else %}
IGNORE
{% endif %}
{% else %}
{# FAILSAFE: Anything else is Real Smoke, Test, or Relay #}
ON
{% endif %}
sensor:
- name: "X-Sense Network Status"
unique_id: "xsense_house_status_text"
state_topic: "rtl_433/+/devices/XSense/rows/0/event"
icon: mdi:smoke-detector-variant
device: *xsense_dev
entity_category: diagnostic
value_template: >-
{% set event = value | string | replace('_', ' ') %}
{% if event.isdigit() %}
Unknown ({{ event }})
{% else %}
{{ event }}
{% endif %}
- name: "X-Sense Last Communication"
unique_id: "xsense_house_last_comms"
state_topic: "rtl_433/+/devices/XSense/time"
device_class: timestamp
entity_category: diagnostic
device: *xsense_dev
value_template: "{{ value }}"
- name: "X-Sense Flag"
unique_id: "xsense_house_flag"
state_topic: "rtl_433/+/devices/XSense/rows/0/flag"
device: *xsense_dev
entity_category: diagnostic
value_template: "{{ '0x%02x' % (value | int) }}"
- name: "X-Sense Raw Message Type"
unique_id: "xsense_house_msg_raw"
state_topic: "rtl_433/+/devices/XSense/rows/0/msg_type"
device: *xsense_dev
entity_category: diagnostic
value_template: "{{ value }}"
How to customize for your specific Network ID
To avoid picking up neighbors, first run the config above. Long-press the test button on one of your units and check the rtl_433 logs to find your 24-bit ID.
Once found, update your decoder match line:
- Original:
match={80}0x55555555555555545898, - Locked:
match={104}0x55555555555555545898FFFFFF,(ReplaceFFFFFFwith your hex code).
Automation to remind you to test the sensors
alias: "Safety: X-Sense 6-Month Manual Test Reminder"
description: >-
Reminds you to press the test button on the X-Sense smoke alarms if it has
been over 180 days since the last RF packet was received.
triggers:
- trigger: time
at: "09:00:00"
- trigger: homeassistant
event: start
conditions:
- condition: template
value_template: >-
{% set last_comms =
states('sensor.x_sense_smoke_alarm_network_x_sense_last_communication') %}
{% if last_comms not in ['unknown', 'unavailable', 'none'] %}
{{ now() - (last_comms | as_datetime) > timedelta(days=180) }}
{% else %}
False
{% endif %}
actions:
- action: persistent_notification.create
data:
title: ⚠️ Smoke Alarm Test Required
message: >
It has been over 6 months since Home Assistant received a signal from
your X-Sense smoke alarm network.
Please press the Test button on any unit. This will verify the mesh
network is working, confirm the RTL-SDR is still receiving RF data, and
automatically reset this 6-month countdown timer.
notification_id: smoke_alarm_test_required
mode: single