local device = {} device.model = "E8331DST300ZB" device.vendor = "Schneider Electric" device.description = "Wiser AvatarOn 1G dimmer switch" device.zigbeeModel = { "E8331DST300ZB" } -- The single dimmable channel lives on endpoint 10 (the device has no endpoint -- 1). Home Assistant's json-schema light always publishes UNSUFFIXED -- {"state","brightness"} to /set and reads the same keys back from the -- state topic, so this is modelled as a plain single-endpoint light (no -- multiEndpoint / no _l1 suffix). Builtin fromZigbee decodes the ep10 reports -- into state/brightness; the on/off + level commands are addressed explicitly -- to endpoint 10 via Lua toZigbee (the builtin path would target endpoint 1, -- which this device ignores -> "cannot control"). local EP = 10 -- Indicator mode lives in the Visa configuration cluster (0xFC04), attribute -- indicatorMode (0x0002, UINT8), Schneider manufacturer code 0x105E. -- E8331 DST value map matches z2m visaIndicatorMode([0,1,2,3]). local INDICATOR_FROM = { [0] = "reverse_with_load", [1] = "consistent_with_load", [2] = "always_off", [3] = "always_on" } local INDICATOR_TO = { reverse_with_load = 0, consistent_with_load = 1, always_off = 2, always_on = 3 } device.exposes = { { type = "light", features = { { type = "binary", name = "state", property = "state", access = 7, value_on = "ON", value_off = "OFF", description = "On/off state of this light", }, { type = "numeric", name = "brightness", property = "brightness", access = 7, min = 0, max = 254, description = "Brightness of this light", }, }, }, { type = "enum", name = "indicator_mode", property = "indicator_mode", access = 7, description = "Set indicator mode for switch", category = "config", values = { "reverse_with_load", "consistent_with_load", "always_off", "always_on" }, }, } device.fromZigbee = { "on_off", "brightness", { -- Visa config cluster: reflect indicator mode changed externally. The Lua -- fromZigbee msg carries no attribute id, so guard on the valid range. cluster = 0xFC04, convert = function(msg) if not msg.is_attribute then return nil end local v = msg.payload and msg.payload[1] local mode = v and INDICATOR_FROM[v] if mode == nil then return nil end return { indicator_mode = mode } end, }, } device.toZigbee = { { key = "state", convert = function(value, endpoint) local cmd if value == "ON" then cmd = 0x01 elseif value == "OFF" then cmd = 0x00 elseif value == "TOGGLE" then cmd = 0x02 else return nil end -- genOnOff (0x0006) command to endpoint 10 return { cluster = 0x0006, endpoint = EP, command = cmd, isAttribute = false, payload = {} } end, }, { key = "brightness", convert = function(value, endpoint) local level = math.floor(tonumber(value) or 0) if level > 254 then level = 254 end if level < 0 then level = 0 end -- genLevelCtrl (0x0008) moveToLevelWithOnOff: [level, transition LE] -- transition 0x000A = 1.0s, matching the builtin brightness default. return { cluster = 0x0008, endpoint = EP, command = 0x04, isAttribute = false, payload = { level, 0x0A, 0x00 } } end, }, { key = "indicator_mode", convert = function(value, endpoint) local n = INDICATOR_TO[value] if n == nil then return nil end -- Visa config cluster 0xFC04, indicatorMode attr 0x0002 (UINT8). return { cluster = 0xFC04, endpoint = EP, isAttribute = true, attrId = 0x0002, attrType = 0x20, manufacturerCode = 0x105E, payload = { n } } end, }, } device.configure = { bind = { "genOnOff", "genLevelCtrl" }, reporting = { { cluster = "genOnOff", attribute = "onOff", min = 0, max = 3600, change = 0, }, { cluster = "genLevelCtrl", attribute = "currentLevel", min = 1, max = 3600, change = 1, }, }, } return device