Here is a simple, 120 lines driver that reports both motion and illuminance and battery :
/*
* IKEA MYGGSPRAY Matter Motion + Illuminance + Battery (minimal)
*
* Last edited: 2025/12/24 10:21 AM
*/
import hubitat.device.HubAction
import hubitat.device.Protocol
metadata {
definition(name: "IKEA MYGGSPRAY Matter Motion+Lux+Bat", namespace: "community", author: "kkossev + ChatGPT :)") {
capability "Sensor"
capability "MotionSensor"
capability "IlluminanceMeasurement"
capability "Refresh"
capability "Initialize"
capability "Battery"
}
preferences {
input name: "txtEnable", type: "bool", title: "Enable descriptionText logging", defaultValue: true
input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: false
}
}
void installed() {
if (logEnable) log.debug "installed()"
initialize()
}
void updated() {
if (logEnable) log.debug "updated()"
if (logEnable) runIn(1800, "logsOff")
initialize()
}
void logsOff() {
device.updateSetting("logEnable", [value: "false", type: "bool"])
log.warn "Debug logging disabled"
}
void initialize() {
if (logEnable) log.debug "initialize()"
subscribeToAttributes()
refresh()
}
void refresh() {
// Read current values (optional but handy after pairing/restart)
List<Map<String,String>> paths = []
paths.add(matter.attributePath(0x01, 0x0400, 0x0000)) // Illuminance MeasuredValue
paths.add(matter.attributePath(0x02, 0x0406, 0x0000)) // Occupancy
paths.add(matter.attributePath(0x00, 0x002F, 0x000C)) // BatPercentRemaining
String cmd = matter.readAttributes(paths)
sendHubCommand(new HubAction(cmd, Protocol.MATTER))
}
private void subscribeToAttributes() {
// Keep it as ONE subscription request with multiple attribute paths.
// Many devices support only a few subscriptions per fabric. :contentReference[oaicite:5]{index=5}
List<Map<String,String>> paths = []
paths.add(matter.attributePath(0x01, 0x0400, 0x0000)) // lux
paths.add(matter.attributePath(0x02, 0x0406, 0x0000)) // motion
paths.add(matter.attributePath(0x00, 0x002F, 0x000C)) // BatteryPercentRemaining
// minReportTime=1s, maxReportTime=0xFFFF (device chooses / periodic refresh)
String cmd = matter.cleanSubscribe(1, 0xFFFF, paths)
sendHubCommand(new HubAction(cmd, Protocol.MATTER))
if (txtEnable) log.info "Subscribed to MYGGSPRAY motion + illuminance + battery"
}
def parse(String description) {
if (logEnable) log.debug "parse: ${description}"
Map msg = matter.parseDescriptionAsMap(description) // :contentReference[oaicite:6]{index=6}
if (!msg) return
// Hubitat’s parsed map keys can vary by message type; we handle common patterns.
// Typical fields you may see: endpoint, cluster, attrId, value
Integer ep = safeHexToInt(msg.endpoint)
Integer clus = safeHexToInt(msg.cluster)
Integer attrId = safeHexToInt(msg.attrId)
String value = msg.value?.toString()
if (ep == null || clus == null || attrId == null) return
// Illuminance Measurement: cluster 0x0400 attr 0x0000 (MeasuredValue)
if (ep == 0x01 && clus == 0x0400 && attrId == 0x0000) {
Integer raw = safeHexToInt(value)
if (raw != null) {
// Matter Illuminance MeasuredValue is in lux * 100
Integer lux = raw / 100.0
String descText = "Illuminance is ${lux} lx"
sendEvent(name: "illuminance", value: lux, unit: "lx", descriptionText: txtEnable ? descText : null)
if (txtEnable) { log.info descText }
}
return
}
// Occupancy Sensing: cluster 0x0406 attr 0x0000 (Occupancy)
if (ep == 0x02 && clus == 0x0406 && attrId == 0x0000) {
Integer occ = safeHexToInt(value)
if (occ != null) {
// bit0 = occupied (motion)
String motion = ((occ & 0x01) != 0) ? "active" : "inactive"
String descText = "Motion is ${motion}"
sendEvent(name: "motion", value: motion, descriptionText: txtEnable ? descText : null)
if (txtEnable) { log.info descText }
}
return
}
// Power Source (Battery): cluster 0x002F attr 0x000C
if (ep == 0x00 && clus == 0x002F && attrId == 0x000C) {
Integer pct = safeHexToInt(value) / 2
if (pct != null) {
// Device is already reporting percentage as an integer (e.g. 0x46 -> 70)
pct = Math.max(0, Math.min(100, pct))
sendEvent(name: "battery", value: pct, unit: "%")
if (txtEnable) log.info "Battery is ${pct}%"
}
return
}
if (logEnable) log.debug "Unhandled Matter report: ${msg}"
}
private Integer safeHexToInt(Object hex) {
if (hex == null) return null
String s = hex.toString().trim()
if (s.startsWith("0x") || s.startsWith("0X")) s = s.substring(2)
if (s == "") return null
try { return Integer.parseUnsignedInt(s, 16) } catch (Exception ignored) { return null }
}
Driver courtesy of ChatGPT
— I just pressed the keys and drank the beer. ![]()