Ikea Blind Group

I have 8 Ikea blinds and they work fairly well. (I modified the code to refresh as others have had to do), but my group dimmer in the dashboard does not seem to activate the blinds. The hub is reporting the following errors.

Any thoughts on required revisions? (The group works fine as a device through voice - Alexa Commands.

That kind of error normally means either there is no command defined in the driver with that name, i.e. setPosition, or based on the capabilities listed in the driver none of them support that command. Does the driver list a command and define an associated method with the name setPosition? We can start there and get into capabilities if that is the case.

The individual device driver functions as it should so the command is defined within that driver.

The error is occurring when using the device group and selecting the blinds as dimmers.

Does that help?

Just thinking out loud but a dimmer would logically have a setLevel but not a setPosition so if one were to equate the two...

Within the group app there is only an option to select bulbs dimmers or switches. So the group app, seeing the blind as a dimmer would send the set level command but not understood by the device driver. Could / should I amend the device driver to define the set level command?

That error though is indicating the set position command is not understood. So when you change the level on the group dimmer would it not send a set level? and then set level would not be understood by the device? It seems to be working opposite...the device tries to send set position to the group?

The following is defined in the driver...

def setLevel(data, rate = null) {

runIn(30,refresh)
runIn(30,ping)
log.info "setLevel()"
def cmd
data = data.toInteger()
if (supportsLiftPercentage()) {
    cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(data, 2))
} else {
    cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(data * 255 / 100), 2))
}

return cmd

}

def setPosition(value){
setLevel(value)
}

Im reading this as set level and set position are assigned??

What capabilities does the driver declare (as ones it implements) at the top? If they didn't declare capability "WindowShade" (or add setPosition as a custom command, but I don't see why you'd do that instead), then that would also cause the above problem, regardless of whether the method is defined in Groovy land.

see below for the full driver code..

/**
*
*

  • Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
  • in compliance with the License. You may obtain a copy of the License at:
  •  http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
  • on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
  • for the specific language governing permissions and limitations under the License.
  • first release for IKEA smart window blinds for hubitat adapted from the driver for ST by Wayne Man
    */
    import hubitat.zigbee.zcl.DataType

metadata {
definition(name: "IKEA Window Blinds", namespace: "ryan780", author: "ryan780", ocfDeviceType: "oic.d.blind", mnmn: "SmartThings", vid: "generic-shade") {
capability "Actuator"
capability "Configuration"
capability "Refresh"
capability "Window Shade"
capability "Health Check"
capability "Switch Level"
capability "Battery"

    command "pause"
    
   	attribute "lastCheckin", "String"
	attribute "lastOpened", "String"

	fingerprint inClusters: "0000,0001,0003,0004", manufacturer: "IKEA of Sweden", model: "FYRTUR block-out roller blind"
}

}

private getCLUSTER_BATTERY_LEVEL() { 0x0001 }
private getCLUSTER_WINDOW_COVERING() { 0x0102 }
private getCOMMAND_OPEN() { 0x00 }
private getCOMMAND_CLOSE() { 0x01 }
private getCOMMAND_PAUSE() { 0x02 }
private getCOMMAND_GOTO_LIFT_PERCENTAGE() { 0x05 }
private getATTRIBUTE_POSITION_LIFT() { 0x0008 }
private getATTRIBUTE_CURRENT_LEVEL() { 0x0000 }
private getCOMMAND_MOVE_LEVEL_ONOFF() { 0x04 }

private List collectAttributes(Map descMap) {
List descMaps = new ArrayList()

descMaps.add(descMap)

if (descMap.additionalAttrs) {
	descMaps.addAll(descMap.additionalAttrs)
}

return descMaps

}

// Parse incoming device messages to generate events
def parse(String description) {
log.debug "description:- ${description}"
def now = new Date().format("yyyy MMM dd EEE h:mm:ss a", location.timeZone)
// send event for heartbeat
sendEvent(name: "lastCheckin", value: now)
if (description?.startsWith("read attr -")) {
Map descMap = zigbee.parseDescriptionAsMap(description)
if (supportsLiftPercentage() && descMap?.clusterInt == CLUSTER_WINDOW_COVERING && descMap.value) {
log.debug "attr: ${descMap?.attrInt}, value: ${descMap?.value}, descValue: ${Integer.parseInt(descMap.value, 16)}, ${device.getDataValue("model")}"
List descMaps = collectAttributes(descMap)
def liftmap = descMaps.find { it.attrInt == ATTRIBUTE_POSITION_LIFT }
if (liftmap && liftmap.value) {
def newLevel = zigbee.convertHexToInt(liftmap.value)
levelEventHandler(newLevel)
}
} else if (!supportsLiftPercentage() && descMap?.clusterInt == zigbee.LEVEL_CONTROL_CLUSTER && descMap.value) {
def valueInt = Math.round((zigbee.convertHexToInt(descMap.value)) / 255 * 100)

        levelEventHandler(valueInt)
    }
	if (descMap?.clusterInt == CLUSTER_BATTERY_LEVEL && descMap.value) {
        log.debug "attr: ${descMap?.attrInt}, value: ${descMap?.value}, descValue: ${Integer.parseInt(descMap.value, 16)}"
        sendEvent(name: "battery", value: Integer.parseInt(descMap.value, 16))
    }
}

}

def levelEventHandler(currentLevel) {
def lastLevel = device.currentValue("level")
log.debug "levelEventHandle - currentLevel: ${currentLevel} lastLevel: ${lastLevel}"
if (lastLevel == "undefined" || currentLevel == lastLevel) { //Ignore invalid reports
log.debug "Ignore invalid reports"
} else {
sendEvent(name: "level", value: currentLevel)
if (currentLevel == 0 || currentLevel == 100) {
sendEvent(name: "windowShade", value: currentLevel == 0 ? "open" : "closed")
} else {
if (lastLevel < currentLevel) {

            sendEvent([name:"windowShade", value: "closing"])
        } else if (lastLevel > currentLevel) {
            sendEvent([name:"windowShade", value: "opening"])
        }
        runIn(1, "updateFinalState", [overwrite:true])
    }
}

}

def updateFinalState() {
def level = device.currentValue("level")
log.debug "updateFinalState: ${level}"
if (level > 0 && level < 100) {
sendEvent(name: "windowShade", value: "partially open")
}
}

def supportsLiftPercentage() {
device.getDataValue("manufacturer") != "Feibit Co.Ltd"
}

def close() {
runIn(30,refresh)
runIn(30,ping)
log.info "close()"
zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_CLOSE)

}

def open() {
runIn(30,refresh)
runIn(30,ping)
log.info "open()"
zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_OPEN)
}

def setLevel(data, rate = null) {

runIn(30,refresh)
runIn(30,ping)
log.info "setLevel()"
def cmd
data = data.toInteger()
if (supportsLiftPercentage()) {
    cmd = zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_GOTO_LIFT_PERCENTAGE, zigbee.convertToHexString(data, 2))
} else {
    cmd = zigbee.command(zigbee.LEVEL_CONTROL_CLUSTER, COMMAND_MOVE_LEVEL_ONOFF, zigbee.convertToHexString(Math.round(data * 255 / 100), 2))
}

return cmd

}

def pause() {
log.info "pause()"
zigbee.command(CLUSTER_WINDOW_COVERING, COMMAND_PAUSE)
}

/**

  • PING is used by Device-Watch in attempt to reach the Device
  • */

def ping() {
return zigbee.readAttribute(CLUSTER_BATTERY_LEVEL, 0x0021) // Read the Battery Level
}

def refresh() {
log.info "refresh()"

def cmds
if (supportsLiftPercentage()) {
    cmds = zigbee.readAttribute(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT)
} else {
    cmds = zigbee.readAttribute(zigbee.LEVEL_CONTROL_CLUSTER, ATTRIBUTE_CURRENT_LEVEL)
}
return cmds

}

def configure() {
// Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time)
log.info "configure()"
sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
log.debug "Configuring Reporting and Bindings."

def cmds
if (supportsLiftPercentage()) {
    cmds = zigbee.configureReporting(CLUSTER_WINDOW_COVERING, ATTRIBUTE_POSITION_LIFT, DataType.UINT8, 0, 600, null)
} else {
    cmds = zigbee.levelConfig()
}
return refresh() + cmds

}

def setPosition(value){
setLevel(value)
}

From what I can tell the shades properly react to a "setposition" command and a "setlevel" command from the device level.

The group also properly reacts to Alexa voice commands. It really just appears to be the group dashboard response / error.

Any thoughts? Thank you!

So a little more testing and it appears as a group item the blinds need to be selected as "dimmer" in lieu of shade. Revising that allows control on the dashboard. I'm not smart enough at this point to describe why, but that is the effect. :slight_smile:

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.