Skip to content

Commit

Permalink
CHAD-4340 Update Window Shade devices to use Window Shade Level
Browse files Browse the repository at this point in the history
  • Loading branch information
dkirker committed Jul 27, 2020
1 parent 7cfa646 commit e938857
Show file tree
Hide file tree
Showing 6 changed files with 614 additions and 512 deletions.
153 changes: 83 additions & 70 deletions devicetypes/axis/axis-gear-st.src/axis-gear-st.groovy
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
import groovy.json.JsonOutput

metadata {
definition (name: "AXIS Gear ST", namespace: "axis", author: "AXIS Labs", ocfDeviceType: "oic.d.blind", vid: "generic-shade-3") {
definition (name: "AXIS Gear ST", namespace: "axis", author: "AXIS Labs", ocfDeviceType: "oic.d.blind", vid: "generic-shade-3") {
capability "Window Shade"
capability "Window Shade Level"
capability "Window Shade Preset"
capability "Switch Level"
capability "Battery"
capability "Refresh"
capability "Health Check"
capability "Actuator"
capability "Configuration"

// added in for Google Assistant Operability
capability "Switch"
capability "Switch"

//Custom Commandes to achieve 25% increment control
command "ShadesUp"
command "ShadesDown"

// command to stop blinds
command "stop"
command "getversion"

fingerprint profileID: "0104", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear
fingerprint profileId: "0104", deviceId: "0202", inClusters: "0000, 0003, 0006, 0008, 0102, 0020, 0001", outClusters: "0019", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear
fingerprint endpointID: "01, C4", profileId: "0104, C25D", deviceId: "0202", inClusters: "0000, 0003, 0006, 0008, 0102, 0020, 0001", outClusters: "0019", manufacturer: "AXIS", model: "Gear", deviceJoinName: "AXIS Window Treatment" //AXIS Gear
Expand All @@ -36,18 +37,18 @@ metadata {
//Updated 2019-08-09 - minor changes and improvements, onoff state reporting fixed
//Updated 2019-11-11 - minor changes
}

tiles(scale: 2) {
multiAttributeTile(name:"windowShade", type: "lighting", width: 3, height: 3) {
tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") {
attributeState("open", label: 'Open', action:"close", icon:"http://i.imgur.com/4TbsR54.png", backgroundColor:"#ffcc33", nextState: "closing")
attributeState("partially open", label: 'Partially Open', action:"close", icon:"http://i.imgur.com/vBA17WL.png", backgroundColor:"#ffcc33", nextState: "closing")
attributeState("closed", label: 'Closed', action:"open", icon:"http://i.imgur.com/mtHdMse.png", backgroundColor:"#bbbbdd", nextState: "opening")
attributeState("opening", label: 'Opening', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ffcc33", nextState: "stopping")
attributeState("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#bbbbdd", nextState: "stopping")
attributeState("stopping", label: 'Stopping', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777")
attributeState("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777")
attributeState("unknown", label: 'Configuring.... Please Wait', icon:"http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777")
attributeState("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#bbbbdd", nextState: "stopping")
attributeState("stopping", label: 'Stopping', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777")
attributeState("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777")
attributeState("unknown", label: 'Configuring.... Please Wait', icon:"http://i.imgur.com/vBA17WL.png", backgroundColor: "#ff7777")
}
tileAttribute ("device.level", key: "VALUE_CONTROL") {
attributeState("VALUE_UP", action: "ShadesUp")
Expand All @@ -61,9 +62,9 @@ metadata {
state("closed", label:'Closed', action:"open", icon:"http://i.imgur.com/SAiEADI.png", backgroundColor:"#bbbbdd", nextState: "opening")
state("opening", label: 'Opening', action: "stop", icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ffcc33", nextState: "stopping")
state("closing", label: 'Closing', action: "stop", icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#bbbbdd", nextState: "stopping")
state("stopping", label: 'Stopping', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777")
state("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777")
state("unknown", label: 'Configuring', icon:"http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777")
state("stopping", label: 'Stopping', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777")
state("stoppingNS", label: 'Stopping Not Supported', icon: "http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777")
state("unknown", label: 'Configuring', icon:"http://i.imgur.com/y0ZpmZp.png", backgroundColor: "#ff7777")
}
controlTile("mediumSlider", "device.level", "slider",decoration:"flat",height:2, width: 2, inactiveLabel: true) {
state("level", action:"switch level.setLevel")
Expand All @@ -86,7 +87,7 @@ metadata {
preferences {
input "preset", "number", title: "Preset position", description: "Set the window shade preset position", defaultValue: 50, required: false, displayDuringSetup: true, range:"1..100"
}

main(["main"])
details(["windowShade", "mediumSlider", "contPause", "home", "version", "battery", "refresh"])
}
Expand Down Expand Up @@ -117,31 +118,31 @@ private getMIN_WINDOW_COVERING_VERSION() {1093}

//Custom command to increment blind position by 25 %
def ShadesUp() {
def shadeValue = device.latestValue("level") as Integer ?: 0
def shadeValue = device.latestValue("shadeLevel") as Integer ?: device.latestValue("level") as Integer ?: 0

if (shadeValue < 100) {
shadeValue = Math.min(25 * (Math.round(shadeValue / 25) + 1), 100) as Integer
}
else {
else {
shadeValue = 100
}
//sendEvent(name:"level", value:shadeValue, displayed:true)
setLevel(shadeValue)
setShadeLevel(shadeValue)
//sendEvent(name: "windowShade", value: "opening")
}

//Custom command to decrement blind position by 25 %
def ShadesDown() {
def shadeValue = device.latestValue("level") as Integer ?: 0
def shadeValue = device.latestValue("shadeLevel") as Integer ?: device.latestValue("level") as Integer ?: 0

if (shadeValue > 0) {
shadeValue = Math.max(25 * (Math.round(shadeValue / 25) - 1), 0) as Integer
}
else {
else {
shadeValue = 0
}
//sendEvent(name:"level", value:shadeValue, displayed:true)
setLevel(shadeValue)
setShadeLevel(shadeValue)
//sendEvent(name: "windowShade", value: "closing")
}

Expand All @@ -160,11 +161,11 @@ def stop() {
}
else {
if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){
return zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE)
return zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE)
}
else {
sendEvent(name: "windowShade", value: "stoppingNS")
return zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, [delay:5000])
return zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, [delay:5000])
}
}
}
Expand All @@ -176,50 +177,47 @@ def pause() {
//Send Command through setLevel()
def on() {
log.info "on()"
sendEvent(name: "windowShade", value: "opening")
sendEvent(name: "switch", value: "on")

if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) {
zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_OPEN)
}
else {
setLevel(100)
}
open()
}

//Send Command through setLevel()
def off() {
log.info "off()"
sendEvent(name: "windowShade", value: "closing")
sendEvent(name: "switch", value: "off")
close()
//zigbee.off()
}

//Command to set the blind position (%) and log the event
def setLevel(value, rate=null) {
log.info "setLevel ($value)"


setShadeLevel(value)
}

def setShadeLevel(value) {
log.info "setShadeLevel ($value)"
Integer currentLevel = state.level

def i = value as Integer
sendEvent(name:"level", value: value, displayed:true)

sendEvent(name:"level", value: value, unit:"%", displayed: false)
sendEvent(name:"shadeLevel", value: value, unit:"%", displayed:true)

if ( i == 0) {
sendEvent(name: "switch", value: "off")
}
else {
sendEvent(name: "switch", value: "on")
}

if (i > currentLevel) {
sendEvent(name: "windowShade", value: "opening")
}
else if (i < currentLevel) {
sendEvent(name: "windowShade", value: "closing")
}
//setWindowShade(i)

if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){
zigbee.command(CLUSTER_WINDOWCOVERING,WINDOWCOVERING_CMD_GOTOLIFTPERCENTAGE, zigbee.convertToHexString(100-i,2))
}
Expand All @@ -236,8 +234,8 @@ def open() {
zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_OPEN)
}
else {
setLevel(100)
}
setShadeLevel(100)
}
}
//Send Command through setLevel()
def close() {
Expand All @@ -247,13 +245,13 @@ def close() {
zigbee.command(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_CMD_CLOSE)
}
else {
setLevel(0)
setShadeLevel(0)
}
}

def presetPosition() {
log.info "presetPosition()"
setLevel(preset ?: state.preset ?: 50)
setShadeLevel(preset ?: state.preset ?: 50)
}

//Reporting of Battery & position levels
Expand All @@ -262,7 +260,7 @@ def ping(){
return refresh()
}

//Set blind State based on position (which shows appropriate image)
//Set blind State based on position (which shows appropriate image)
def setWindowShade(value) {
if ((value>0)&&(value<99)){
sendEvent(name: "windowShade", value: "partially open", displayed:true)
Expand All @@ -279,32 +277,32 @@ def setWindowShade(value) {
def refresh() {
log.debug "parse() refresh"
def cmds_refresh = null

if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION){
cmds_refresh = zigbee.readAttribute(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE)
}
else {
cmds_refresh = zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL)

}
cmds_refresh = cmds_refresh +

cmds_refresh = cmds_refresh +
zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY) +
zigbee.readAttribute(CLUSTER_BASIC, BASIC_ATTR_SWBUILDID)

log.info "refresh() --- cmds: $cmds_refresh"

return cmds_refresh
}

def getversion () {
//state.currentVersion = 0
sendEvent(name: "version", value: "Checking Version ... ")
sendEvent(name: "version", value: "Checking Version ... ")
return zigbee.readAttribute(CLUSTER_BASIC, BASIC_ATTR_SWBUILDID)
}

//configure reporting
def configure() {
def configure() {
state.currentVersion = 0
sendEvent(name: "windowShade", value: "unknown")
log.debug "Configuring Reporting and Bindings."
Expand All @@ -316,33 +314,44 @@ def configure() {
zigbee.readAttribute(CLUSTER_ONOFF, ONOFF_ATTR_ONOFFSTATE) +
zigbee.readAttribute(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL) +
zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY)
def cmds = zigbee.configureReporting(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE, 0x20, 1, 3600, 0x00) +

def cmds = zigbee.configureReporting(CLUSTER_WINDOWCOVERING, WINDOWCOVERING_ATTR_LIFTPERCENTAGE, 0x20, 1, 3600, 0x00) +
zigbee.configureReporting(CLUSTER_ONOFF, ONOFF_ATTR_ONOFFSTATE, 0x10, 1, 3600, 0x00) +
zigbee.configureReporting(CLUSTER_LEVEL, LEVEL_ATTR_LEVEL, 0x20, 1, 3600, 0x00) +
zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY, 0x20, 1, 3600, 0x01)

log.info "configure() --- cmds: $cmds"
return attrs_refresh + cmds
}

def parse(String description) {
log.trace "parse() --- description: $description"

Map map = [:]

if (device.currentValue("shadeLevel") == null && device.currentValue("level") != null) {
sendEvent(name: "shadeLevel", value: device.currentValue("level"), unit: "%")
}

def event = zigbee.getEvent(description)
if (event && description?.startsWith('on/off')) {
log.trace "sendEvent(event)"
sendEvent(event)
}

else if ((description?.startsWith('read attr -')) || (description?.startsWith('attr report -'))) {
map = parseReportAttributeMessage(description)
def result = map ? createEvent(map) : null

if (map.name == "level") {
def shadeLevelMap = map.findAll { it.key != "displayed" }
shadeLevelMap.name = "shadeLevel"
result = [result, createEvent(shadeLevelMap)]
}

log.debug "parse() --- returned: $result"
return result
}
}
}

private Map parseReportAttributeMessage(String description) {
Expand All @@ -366,23 +375,27 @@ private Map parseReportAttributeMessage(String description) {
//Set icon based on device feedback for the open, closed, & partial configuration
resultMap.value = levelValue
state.level = levelValue
resultMap.unit = "%"
resultMap.displayed = false
setWindowShade(levelValue)
}
else if (descMap.clusterInt == CLUSTER_LEVEL && descMap.attrInt == LEVEL_ATTR_LEVEL) {
//log.debug "parse() --- returned level :$state.currentVersion "
def currentLevel = state.level
def currentLevel = state.level

resultMap.name = "level"
def levelValue = Math.round(Integer.parseInt(descMap.value, 16))
def levelValuePercent = Math.round((levelValue/255)*100)
//Set icon based on device feedback for the open, closed, & partial configuration
resultMap.value = levelValuePercent
state.level = levelValuePercent

resultMap.unit = "%"
resultMap.displayed = false

if (state.currentVersion >= MIN_WINDOW_COVERING_VERSION) {
//Integer currentLevel = state.level
sendEvent(name:"level", value: levelValuePercent, displayed:true)
sendEvent(name:"level", value: levelValuePercent, unit: "%", displayed: false)

if (levelValuePercent > currentLevel) {
sendEvent(name: "windowShade", value: "opening")
} else if (levelValuePercent < currentLevel) {
Expand All @@ -396,21 +409,21 @@ private Map parseReportAttributeMessage(String description) {
else if (descMap.clusterInt == CLUSTER_BASIC && descMap.attrInt == BASIC_ATTR_SWBUILDID) {
resultMap.name = "version"
def versionString = descMap.value

StringBuilder output = new StringBuilder("")
StringBuilder output2 = new StringBuilder("")

for (int i = 0; i < versionString.length(); i += 2) {
String str = versionString.substring(i, i + 2)
output.append((char) (Integer.parseInt(str, 16)))
output.append((char) (Integer.parseInt(str, 16)))
if (i > 19) {
output2.append((char) (Integer.parseInt(str, 16)))
}
}
}

def current = Integer.parseInt(output2.toString())
state.currentVersion = current
resultMap.value = output.toString()
resultMap.value = output.toString()
}
else {
log.debug "parseReportAttributeMessage() --- ignoring attribute"
Expand Down
Loading

0 comments on commit e938857

Please sign in to comment.