Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CHAD-4340 Update Window Shade devices to use Window Shade Level #30151

Merged
merged 1 commit into from
Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 81 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)"


tpmanley marked this conversation as resolved.
Show resolved Hide resolved
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the unit necessary here? we don't have it anywhere else we use switch level afaik

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unit is defaulted to %. I'm preferring to be pedantic with the use of units.

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,42 @@ 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") {
result = [result, createEvent([name: "shadeLevel", value: map.value, unit: map.unit])]
}

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

private Map parseReportAttributeMessage(String description) {
Expand All @@ -366,23 +373,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 +407,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