Getting tile label Unknown

Hi
getting started with tiles and DH and have this DH that I’m using to control my roller shutter (shelly 2 module)
I have 2 issues that i don’t know how to solve :

  • when operating the roller shutter manually or from the shelly app I get the main tile label as Unknown after pressing the refresh while the percentage value is right.
    I can see from the logs that it create the correct event
25c48e96-b67c-4edf-af45-323a09967d14  7:26:30 PM: debug State stop
25c48e96-b67c-4edf-af45-323a09967d14  7:26:30 PM: debug Position 77%
25c48e96-b67c-4edf-af45-323a09967d14  7:26:30 PM: debug Parsing result XXX
25c48e96-b67c-4edf-af45-323a09967d14  7:26:30 PM: debug Refresh - Getting Status
  • When I operate the device outside of ST (shelly app or manually ) I’m not getting any logs so the state and percentage stay until I push the refresh on ST (and then I get Unknown but perc is correct )
    should I use “Subscribing to device Events” ? How can I see that stat has changed not by ST .

Here is the DH code:

/**
 *  Shelly 2 as Roller Shutter Device Handler
 *
 *  Copyright 2018 DUCCIO GASPARRI
 *
 *  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.
 *
 *
 *
 * Shelly http POST at http://IP/roller/0 (roller 1 does not exist) with body form-urlencoded:
 *   go=open
 *   go=close
 *   go=to_pos&roller_pos=[value 0-100]
 *   go=stop
 *
 */
 
 

metadata {
	definition (name: "Shelly 2 as Roller Shutter", namespace: "dgasparri", author: "Duccio Marco Gasparri") {
		capability "Actuator"
		capability "Sensor"
        capability "Refresh" // refresh command
        capability "Health Check"
        capability "Switch Level" // attribute: level (integer, setter: setLevel), command setLevel(level)
        capability "Switch"
        capability "Window Shade" // windowShade.value ( closed, closing, open, opening, partially open, unknown ), methods: close(), open(), presetPosition()
    
        // @TODO: this IP or preferences IP?
	    // attribute "IP", "string"
        command "stop"
	}



	tiles(scale: 2) {
        multiAttributeTile(name:"windowShade", type: "generic", width: 6, height: 4){
            tileAttribute ("device.windowShade", key: "PRIMARY_CONTROL") {
                attributeState "unknown", label:'${name}', action:"close", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"closing"
                attributeState "open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing"
                attributeState "closed", label:'${name}', action:"open", icon:"st.shades.shade-closed", backgroundColor:"#ffffff", nextState:"opening"
                attributeState "partially open", label:'${name}', action:"close", icon:"st.shades.shade-open", backgroundColor:"#79b821", nextState:"closing"
                attributeState "opening", label:'${name}', action:"stop", icon:"st.shades.shade-opening", backgroundColor:"#79b821", nextState:"partially open"
                attributeState "closing", label:'${name}', action:"stop", icon:"st.shades.shade-closing", backgroundColor:"#ffffff", nextState:"partially open"
            }
            tileAttribute ("device.level", key: "SLIDER_CONTROL") {
                attributeState "level", action:"setLevel", label:'${currentValue} %'
            }
        }

        standardTile("up", "device.level", width: 2, height: 2, decoration: "flat") {
            state "default", label: "open", action:"open", icon:"st.shades.shade-open"
        }

        standardTile("home", "device.level", width: 2, height: 2, decoration: "flat") {
            state "default", label: "preset", action:"presetPosition", icon:"st.Home.home9" //st.Home.home9
        }

        standardTile("down", "device.level", width: 2, height: 2, decoration: "flat") {
            state "default", label: "close", action:"close", icon:"st.shades.shade-closed"
        }


        standardTile("refresh", "device.refresh", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh", nextState: "disabled"
            state "disabled", label:'', action:"", icon:"st.secondary.refresh"
        }


        preferences {
            input("ip", "string", title:"IP", description:"Shelly IP Address", defaultValue:"" , required: false, displayDuringSetup: true)
            input("preset", "number", title: "Pre-defined position (1-100)", defaultValue: 50, required: false, displayDuringSetup: true)
            input("closedif", "number", title: "Closed if at most (1-100)", defaultvalue: 5, required: false, displayDuringSetup: false)
            input("openif", "number", title: "Open if at least (1-100)", defaultvalue: 85, required: false, displayDuringSetup: false)

        }

        main(["windowShade"])
        details(["windowShade", "up", "home", "down", "refresh"])
	}
}


def getCheckInterval() {
    // These are battery-powered devices, and it's not very critical
    // to know whether they're online or not – 12 hrs
    log.debug "getCheckInterval"
    return 4 * 60 * 60
}

def installed() {
    log.debug "Installed"
    sendEvent(name: "checkInterval", value: checkInterval, displayed: false)
    refresh()
}

def updated() {
    log.debug "Updated"
    if (device.latestValue("checkInterval") != checkInterval) {
        sendEvent(name: "checkInterval", value: checkInterval, displayed: false)
    }
    refresh()
}


def parseLanMessage(description) {
    log.debug "Parsing result $description"
    
    def msg = parseLanMessage(description)

    // log.debug "Lan message $msg"
    // headers:[content-length:172, http/1.1 200 ok:null, connection:close, content-type:application/json, server:Mongoose/6.11], 
    // body:{"state":"close","power":0.00,"is_valid":true,"safety_switch":false,"stop_reason":"normal",
    //    "last_direction":"close","current_pos":46,"calibrating":false,"positioning":true}, 
    // header:HTTP/1.1 200 OK 
    
 
    def headersAsString = msg.header // => headers as a string
    def headerMap = msg.headers      // => headers as a Map
    def body = msg.body              // => request body as a string
    def status = msg.status          // => http status code of the response
    def data = msg.data              // => either JSON or XML in response body (whichever is specified by content-type header in response)
    
    log.debug "Position ${data.current_pos}%"
    log.debug "State ${data.state}"
    
    def evt1 = createEvent(name: "level", value: data.current_pos, displayed: false)
    def evt2 = null
    def evt3 = null
    if ( data.current_pos < closedif ) {
        log.debug "CreateEvent closed"
        evt2 = createEvent(name: "windowShade", value: "closed", displayed: false)
        evt3 = createEvent(name: "switch", value: "off", displayed: false)
    } else  if ( data.current_pos > openif ) {
        log.debug "CreateEvent open"
        evt2 = createEvent(name: "windowShade", value: "on", displayed: false)
        evt3 = createEvent(name: "switch", value: "on", displayed: false)
    } else {
        log.debug "CreateEvent Partially open"
        evt2 = createEvent(name: "windowShade", value: "partially open", displayed: false)
        evt3 = createEvent(name: "switch", value: "on", displayed: false)
    }

    //log.debut "Parsed to ${evt1.inspect()} and ${evt2.inspect()}"
    return [evt1, evt2, evt3]
}




def open() {
    log.debug "Executing 'open'"
    sendRollerCommand "go=open"
}

def close() {
    log.debug "Executing 'close'"
    sendRollerCommand "go=close"
}

//switch.on
def on() {
    log.debug "Executing switch.on"
    open()
}

//switch.off
def off() {
    log.debug "Executing switch.off"
    close()
}

def setLevel(value, duration = null) {
    log.debug "Executing setLevel value with $value"
    sendRollerCommand "go=to_pos&roller_pos="+value
}

def presetPosition() {
    log.debug "Executing 'presetPosition'"
    setLevel(preset)
}

def stop() {
    log.debug "Executing stop()"
    sendRollerCommand "go=to_pos&roller_pos="+value
}

def ping() {
    log.debug "Ping"
    refresh()
}

def refresh() {
    log.debug "Refresh - Getting Status"
    sendHubCommand(new physicalgraph.device.HubAction(
      method: "GET",
      path: "/roller/0",
      headers: [
        HOST: getShellyAddress(),
        "Content-Type": "application/x-www-form-urlencoded"
      ]
    ))
}

def sendRollerCommand(action) {
    log.debug "Calling /roller/0 with $action"
    sendHubCommand(new physicalgraph.device.HubAction(
      method: "POST",
      path: "/roller/0",
      body: action,
      headers: [
        HOST: getShellyAddress(),
        "Content-Type": "application/x-www-form-urlencoded"
      ]
    ))
    runIn(25, refresh)
}


private getShellyAddress() {
    def port = 80
    def iphex = ip.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join().toUpperCase()
    def porthex = String.format('%04x', port.toInteger())
    def shellyAddress = iphex + ":" + porthex
    device.deviceNetworkId = shellyAddress.toUpperCase()
    log.debug "Using IP " + ip + ", PORT 80 and HEX ADDRESS " + shellyAddress + " for device: ${device.id}"
    return device.deviceNetworkId
}

Thanks to dgasparri who Created this DH

Found the cause for the Unknown value .

the value needs to be open and not on .

So now I have only one thing that I don’t know how to handle.
When I use the physical button or the shelly app to open close the roller shutter I don’t get any status in ST so I cannot sync the tile value .
Only when I press the refresh it get the correct values from the module .
should I use “Subscribing to device Events” ? How can I see that stat has changed not by ST ?

Anyone can help ?

If the device doesn’t have a method of notifying ST of changes in state, you might need to setup device polling for external changes to be reflected in the ST state.

Thanks
I’m trying to check if the device support it but struggling doing it .
Didn’t find something in the docs
http://shelly-api-docs.shelly.cloud/#shelly2-status

Device polling is to get the status every some interval right ? this is not efficient but maybe my last option .

Can you have a look and see if the device support notifying ?