Refresh


#7

Okay, that's not what I asked. Since you are using two method for presence, how are you combining them to know if you are present or not? Or are you just using them independently and both your phone and ST sensor have to be gone for you to be gone?


#8

Yeah, I'm using Chrome. But I thought I did see some tiles update. Maybe not, maybe the page refreshed. I will need to do some more testing.


#9

ST sensor is for guests. Wife & I use our phones using Life360, pending the new app. All 3 are displayed as dashboard tiles.


#10

Okay...I thought you meant that you were using both for yourself.
So, for the ST presence sensor, there's a driver that has Enable and disable like a normal switch.

import groovy.json.JsonOutput

/**
 *
 * ST Arrival Sensor (Advanced)
 *
 *  Copyright 2019 Ryan Casler
 *  Developed from LLWarrenP's "Arrival Sensor HA with Disable"
 *
 *  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.
 *
 */
 
def dthVersion() {
	return "1.0"
}

/*
* Change Log:
* 2019-3-13  - (1.0) Initial release
*/


metadata {
    definition (name: "ST Arrival Sensor (Advanced)", namespace: "ryan780", author: "ryan780") {
        capability "Tone"
        capability "Actuator"
        capability "Presence Sensor"
        capability "Sensor"
        capability "Battery"
        capability "Configuration"
		capability "Switch"

		attribute "enabled", "string"
        command "enable"
        command "disable"
        command "toggle"
		command "disableDelayedEnable", ["minutes"]

        fingerprint inClusters: "0000,0001,0003,000F,0020", outClusters: "0003,0019",
                        manufacturer: "SmartThings", model: "tagv4", deviceJoinName: "Arrival Sensor"
    }

    preferences {
        section {
            image(name: 'educationalcontent', multiple: true, images: [
                "http://cdn.device-gse.smartthings.com/Arrival/Arrival1.png",
                "http://cdn.device-gse.smartthings.com/Arrival/Arrival2.png"
                ])
        }
        section {
            input "checkInterval", "enum", title: "Presence timeout (minutes)", description: "Tap to set",
                    defaultValue:"2", options: ["2", "3", "5"], displayDuringSetup: false
        }
        section {
            input "disabledMode", "enum", title: "When disabling sensor, set presence to:", description: "Tap to set",
                    defaultValue:"auto", options: ["auto", "present", "not present"], displayDuringSetup: false
            input "enabledMode", "enum", title: "When enabling sensor, set presence to:", description: "Tap to set",
                    defaultValue:"auto", options: ["auto", "present", "not present"], displayDuringSetup: false
			input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: true
        }
    }
}

def updated() {
    startTimer()
	log.info "updated..."
    log.warn "debug logging is: ${logEnable == true}"
    if (logEnable) runIn(1800, logsOff)
}


def installed() {
    // Arrival sensors only goes OFFLINE when Hub is off
    sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
}

def logsOff() {
    log.warn "debug logging disabled..."
    device.updateSetting("logEnable", [value: "false", type: "bool"])
}

def configure() {
    def cmds = zigbee.readAttribute(zigbee.POWER_CONFIGURATION_CLUSTER, 0x0020) + zigbee.batteryConfig(20, 20, 0x01)
    if (logEnable) log.debug "configure -- cmds: ${cmds}"
    return cmds
}

def beep() {
    if (logEnable) log.debug "Sending Identify command to beep the sensor for 5 seconds"
    return zigbee.command(0x0003, 0x00, "0500")
}

def parse(String description) {
    state.lastCheckin = now()
    handlePresenceEvent(true)

    if (description?.startsWith('read attr -')) {
        handleReportAttributeMessage(description)
    }

    return []
}

private handleReportAttributeMessage(String description) {
    def descMap = zigbee.parseDescriptionAsMap(description)
    if (descMap.clusterInt == 0x0001 && descMap.attrInt == 0x0020) {
        handleBatteryEvent(Integer.parseInt(descMap.value, 16))
    }
}

/**
 * Create battery event from reported battery voltage.
 *
 * @param volts Battery voltage in .1V increments
 */
private handleBatteryEvent(volts) {
	def descriptionText
    if (volts == 0 || volts == 255) {
        log.debug "Ignoring invalid value for voltage (${volts/10}V)"
    }
    else {
        def batteryMap = [28:100, 27:100, 26:100, 25:90, 24:90, 23:70,
                          22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0]
        def minVolts = 15
        def maxVolts = 28

        if (volts < minVolts)
            volts = minVolts
        else if (volts > maxVolts)
            volts = maxVolts
        def value = batteryMap[volts]
        if (value != null) {
            def linkText = getLinkText(device)
            descriptionText = '{{ linkText }} battery was {{ value }}'
            def eventMap = [
                name: 'battery',
                value: value,
                descriptionText: descriptionText,
                translatable: true
            ]
            if (logEnable) log.debug "Creating battery event for voltage=${volts/10}V: ${linkText} ${eventMap.name} is ${eventMap.value}%"
            sendEvent(eventMap)
        }
    }
}

private handlePresenceEvent(present) {
    def wasPresent = device.currentState("presence")?.value == "present"
    if (!wasPresent && present) {
        if (logEnable) log.debug "Sensor is present"
        startTimer()
    } else if (!present) {
        if (logEnable) log.debug "Sensor is not present"
        stopTimer()
    }
    def linkText = getLinkText(device)
    def descriptionText
    def enabledStatus = ""
    if ( present )
    	descriptionText = "{{ linkText }} has arrived"
    else
    	descriptionText = "{{ linkText }} has left"
    if ((device.currentValue("enabled") == "disabled-present") || (device.currentValue("enabled") == "disabled-not present")) {
    	// Device is disabled so we won't generate a presence event but instead just track the status behind the scenes by generating an enabled event
    	if (logEnable) log.debug "${linkText} is ${device.currentValue("enabled")}: not creating presence event"
        enabledStatus = "disabled-"
        enabledStatus = enabledStatus.concat(present ? "present" : "not present")
		if (device.currentValue("enabled") != enabledStatus) sendEvent(name: "enabled", value: enabledStatus, isStateChange: true)
	}
    else {
    	// Device is enabled so we will generate a presence event and an enabled event
	    def eventMap = [
        	name: "presence",
        	value: present ? "present" : "not present",
        	linkText: linkText,
        	descriptionText: descriptionText,
        	translatable: true
    		]
        enabledStatus = "enabled-"
        enabledStatus = enabledStatus.concat(present ? "present" : "not present")
		if (device.currentValue("enabled") != enabledStatus) sendEvent(name: "enabled", value: enabledStatus, isStateChange: true)
	   	if (logEnable)log.debug "Creating presence event: ${device.displayName} ${eventMap.name} is ${eventMap.value} with status ${device.currentValue("enabled")}"
    	sendEvent(eventMap)
    }
}

private startTimer() {
    if (logEnable)log.debug "Scheduling periodic timer"
    runEvery1Minute("checkPresenceCallback")
}

private stopTimer() {
    if (logEnable)log.debug "Stopping periodic timer"
    unschedule()
}

def checkPresenceCallback() {
    def timeSinceLastCheckin = (now() - state.lastCheckin) / 1000
    def theCheckInterval = (checkInterval ? checkInterval as int : 2) * 60
    if (logEnable)log.debug "Sensor checked in ${timeSinceLastCheckin} seconds ago"
    if (timeSinceLastCheckin >= theCheckInterval) {
        handlePresenceEvent(false)
    }
}

def toggle() {
	// Button pressed, toggle the enabled state (which also tracks the current presence)
	if ((device.currentValue("enabled") == "enabled-present") || (device.currentValue("enabled") == "enabled-not present"))
    	disable()
    else
    	enable()
}

def enable() {
    // Force presence per user settings
    if (logEnable)log.debug "Setting sensor presence to ${settings.enabledMode}"
	sendEvent(name: "switch", value: "on", isStateChange: true)
	if (settings.enabledMode && (settings.enabledMode != "auto")) {
    	stopTimer()
    	sendEvent(name: "presence", value: settings.enabledMode, translatable: true)
        }
    else if (settings.enabledMode && (settings.enabledMode == "auto"))
	    startTimer()
	// Enable the device and update the enabled status to reflect the new status
	if (logEnable)log.debug "Enabling ${getLinkText(device)}"
    if (device.currentValue("presence") == "present")
		sendEvent(name: "enabled", value: "enabled-present", isStateChange: true)
    else if (device.currentValue("presence") == "not present")
		sendEvent(name: "enabled", value: "enabled-not present", isStateChange: true)
}

def disable() {
    // Force presence per user settings
    if (logEnable)log.debug "Setting sensor presence to ${settings.disabledMode}"
	sendEvent(name: "switch", value: "off", isStateChange: true)
	if (settings.disabledMode && (settings.disabledMode != "auto")) {
    	stopTimer()
    	sendEvent(name: "presence", value: settings.disabledMode, translatable: true)
        }
    else if (settings.disabledMode && (settings.disabledMode == "auto"))
	    startTimer()
	// Disable the device and update the enabled status to reflect the new status
	if (logEnable)log.debug "Disabling ${getLinkText(device)}"
    state.updatePresence = false
    if (device.currentValue("presence") == "present")
		sendEvent(name: "enabled", value: "disabled-present", isStateChange: true)
    else if (device.currentValue("presence") == "not present")
		sendEvent(name: "enabled", value: "disabled-not present", isStateChange: true)
}

def disableDelayedEnable(delay) {
    unschedule()
	disable()
	myDelay=(delay.toInteger())*60
	runIn(myDelay, enable)
}

def on(){
	enable()
}

def off(){
	disable()
}

When disabled you can have the presence be whatever you choose in the driver. If you're using life 360 for your other presence, that's going to be a little tougher since that uses a built in app and driver that no one but hubitat staff has access to. You best bet might be to look at Combined Presence. it's a Cobra app. That way, you can control what state the virtual presence sensor you create is in manually. That's the only way I can think to accomplish what you want. Not a clean method to be sure. Might just be easier to get a powerbank to charge your phone up.

I have to say, when I first read your post and read "left my phone at home" i thought at first you were joking. The thought of leaving home without my phone has become almost unthinkable. lol


#11

So I just arrived at the property. The ST sensor updated fine. No refresh needed. Neither of our tiles for Life360 presence updated and the Mode tiles also still hasn't updated since yesterday evening (to night or now to day). A refresh fixes all issues.

Driving there:

Arrived:

Browser refresh:

Sorry if I'm doing something wrong or don't have it set up correctly. I've only started playing with dashboards in the last week or so and have only had the hub less than a month. Still learning...


#12

Yeah, understood. My phone is glued to my hip or hand always. It's just that the battery died and so didn't update location and when I got home I put the phone on charge and used a chromebook. Then noticed the presence hadn't updated (of course because the phone was dead) but then found I couldn't manually set it by click on the tile which was a surprise. But I guess since it takes its data from Life360 and should update automatically it would be stupid for me to ask to overwrite it manually probably anyway.


#13

Are you saying that the dashboard isn't updating or your actual presence isn't updating? You have to be more specific what you are trying to fix. You have to compare what is displayed in the dashboard, which is an app, versus what is displayed in the edit device page. Then we can figure out if your problem is the tile not updating in the dashboard or Life 360 not updating. But you also have to remember, life 360 is FAR from instant.


#14

Again, no update to the tiles until I refresh the browser (eg. status is correct in the device on the hub, dashboard doesn't automatically update either the mode or the presence from Life360). I don't know how to make it any clearer, sorry.

Regarding mode, take a look at the top tile. It still says evening and it's midday. It didn't update until a browser refresh. So this is not just an issue regarding Life360.


#15

Do you have data saving activated in chrome?


#16

Yes! (well its referred to as "Lite Mode" in the menu but refers to data saving). Good idea. Lemme try if that fixes it.


#17

I confirm the problem still exists with Lite Mode switched off on Chrome on my Galaxy S8 Edge. The (Life360) presence tiles and the Mode tile still don't update on my phone (even tho they are shown correctly updated in the Hub device settings).


#18

Android V9 on Galaxy S8 Edge, Chrome v74.


#19

I also confirm the phone power saver mode is off (as required by Life360)


#20

I have a LG G6 android V7. Have the same issue. Tiles on both local and cloud don't update without a refresh. Some of the time the local will update but the cloud link never seems to. For what it's worth, I also have a Sharptools dashboard for comparison. It seems to be fine.

I have some local dashboards on a couple tablets on the wall and they seem to update fine.


#21

Hmm, now it's all just worked perfectly as I drove away from the property. The ST presence sensor tile updated (as it has previously), followed by the 2 Life360 tiles (which did not before). The swimming pool lights then went out as expected. Not sure why it wasn't working before but seems good now. Will have to check if the Mode tile also updates later tonight when it moves from Evening to Night.


#22

Mode tile still didn't update on my Android phone. Updated fine without a browser refresh on an Asus Chromebook I have here. Seems there is something weird going on with my phone, along with others on this thread...


#23

I agree, I think it is a phone issue not an HE issue.


#24

It's an HE Dashboard issue because it doesn't run on my phone! :slight_smile: Meaning, I'm sure Google ain't going to be fixing this for me :sob:


#25

Please provide the rest of the information requested, feel free to send the cloud url in a PM if you would like.


#26

Sent. Thank you!

Not sure what else you need. I believe all your other questions are covered above...