When dryer done send message to ubi

I have an ubi that I would like to have say a message when my smartsense multi is no longer vibrating (ie the dryer is done). There is no direct trigger in Ubi to allow for vibration, and many folks are using virtual on-off tiles to trigger the announcement http://forum.theubi.com/viewtopic.php?f=4&t=221

I have a virtual tile set up, but I don’t know what app to use. I looked at ‘notify me when’ but it only has a trigger for acceleration detected (not no longer detected). Any suggestions on how to modify the laundry app (or write a new app) to either trigger an ubi announcement or turn off the virtual switch? I’m no coder!!

Thanks

Try this on for size

1 Like

Unfortunately that one is using power to detect when the dryer is complete, not vibration. Can anyone help me modify the code of the Laundry Monitor to include a voice announcement?

indent preformatted text by 4 spaces

/**

  • Laundry Monitor
  • Author: SmartThings
  • Sends a message and (optionally) turns on or blinks a light to indicate that laundry is done.
  • Date: 2013-02-21
    */

definition(
name: “Laundry Monitor”,
namespace: “smartthings”,
author: “SmartThings”,
description: “Sends a message and (optionally) turns on or blinks a light to indicate that laundry is done.”,
category: “Convenience”,
iconUrl: “https://s3.amazonaws.com/smartapp-icons/FunAndSocial/App-HotTubTuner.png”,
iconX2Url: “https://s3.amazonaws.com/smartapp-icons/FunAndSocial/App-HotTubTuner%402x.png
)

preferences {
section(“Tell me when this washer/dryer has stopped…”){
input “sensor1”, “capability.accelerationSensor”
}
section(“Via this number (optional, sends push notification if not specified)”){
input(“recipients”, “contact”, title: “Send notifications to”) {
input “phone”, “phone”, title: “Phone Number”, required: false
}
}
section(“And by turning on these lights (optional)”) {
input “switches”, “capability.switch”, required: false, multiple: true, title: "Which lights?"
input “lightMode”, “enum”, options: [“Flash Lights”, “Turn On Lights”], required: false, defaultValue: “Turn On Lights”, title: “Action?”
}
section(“Time thresholds (in minutes, optional)”){
input “cycleTime”, “decimal”, title: “Minimum cycle time”, required: false, defaultValue: 10
input “fillTime”, “decimal”, title: “Time to fill tub”, required: false, defaultValue: 5
}
}

def installed()
{
initialize()
}

def updated()
{
unsubscribe()
initialize()
}

def initialize() {
subscribe(sensor1, “acceleration.active”, accelerationActiveHandler)
subscribe(sensor1, “acceleration.inactive”, accelerationInactiveHandler)
}

def accelerationActiveHandler(evt) {
log.trace "vibration"
if (!state.isRunning) {
log.info "Arming detector"
state.isRunning = true
state.startedAt = now()
}
state.stoppedAt = null
}

def accelerationInactiveHandler(evt) {
log.trace "no vibration, isRunning: $state.isRunning"
if (state.isRunning) {
log.debug "startedAt: ${state.startedAt}, stoppedAt: ${state.stoppedAt}"
if (!state.stoppedAt) {
state.stoppedAt = now()
def delay = Math.floor(fillTime * 60).toInteger()
runIn(delay, checkRunning, [overwrite: false])
}
}
}

def checkRunning() {
log.trace "checkRunning()"
if (state.isRunning) {
def fillTimeMsec = fillTime ? fillTime * 60000 : 300000
def sensorStates = sensor1.statesSince(“acceleration”, new Date((now() - fillTimeMsec) as Long))

	if (!sensorStates.find{it.value == "active"}) {

		def cycleTimeMsec = cycleTime ? cycleTime * 60000 : 600000
		def duration = now() - state.startedAt
		if (duration - fillTimeMsec > cycleTimeMsec) {
			log.debug "Sending notification"

			def msg = "${sensor1.displayName} is finished"
			log.info msg

            if (location.contactBookEnabled) {
                sendNotificationToContacts(msg, recipients)
            }
            else {

                if (phone) {
                    sendSms phone, msg
                } else {
                    sendPush msg
                }

            }

			if (switches) {
				if (lightMode?.equals("Turn On Lights")) {
					switches.on()
				} else {
					flashLights()
				}
			}
		} else {
			log.debug "Not sending notification because machine wasn't running long enough $duration versus $cycleTimeMsec msec"
		}
		state.isRunning = false
		log.info "Disarming detector"
	} else {
		log.debug "skipping notification because vibration detected again"
	}
}
else {
	log.debug "machine no longer running"
}

}

private flashLights() {
def doFlash = true
def onFor = onFor ?: 1000
def offFor = offFor ?: 1000
def numFlashes = numFlashes ?: 3

log.debug "LAST ACTIVATED IS: ${state.lastActivated}"
if (state.lastActivated) {
	def elapsed = now() - state.lastActivated
	def sequenceTime = (numFlashes + 1) * (onFor + offFor)
	doFlash = elapsed > sequenceTime
	log.debug "DO FLASH: $doFlash, ELAPSED: $elapsed, LAST ACTIVATED: ${state.lastActivated}"
}

if (doFlash) {
	log.debug "FLASHING $numFlashes times"
	state.lastActivated = now()
	log.debug "LAST ACTIVATED SET TO: ${state.lastActivated}"
	def initialActionOn = switches.collect{it.currentSwitch != "on"}
	def delay = 1L
	numFlashes.times {
		log.trace "Switch on after  $delay msec"
		switches.eachWithIndex {s, i ->
			if (initialActionOn[i]) {
				s.on(delay: delay)
			}
			else {
				s.off(delay:delay)
			}
		}
		delay += onFor
		log.trace "Switch off after $delay msec"
		switches.eachWithIndex {s, i ->
			if (initialActionOn[i]) {
				s.off(delay: delay)
			}
			else {
				s.on(delay:delay)
			}
		}
		delay += offFor
	}
}

}

sure can I’ll do it for you tomorrow

give this a try:

/**
 *  Alert on Power Consumption
 *
 *  Copyright 2014 George Sudarkoff
 *
 *  Updated to work with acceleration sensor. -- Tim Slagle 
 *
 *  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.
 *
 */

import groovy.time.*

definition(
  name: "Better Laundry Monitor (Acceleration)",
  namespace: "com.sudarkoff",
  author: "George Sudarkoff",
  description: "Using a switch with powerMonitor capability, monitor the laundry cycle and alert when it's done.",
  category: "Green Living",
  iconUrl: "https://s3.amazonaws.com/smartthings-device-icons/Appliances/appliances8-icn.png",
  iconX2Url: "https://s3.amazonaws.com/smartthings-device-icons/Appliances/appliances8-icn@2x.png")


preferences {
  section ("When this device stops sensing vibration") {
    input "acceleration", "capability.accelerationSensor", multiple: false, required: true
    input "cycle_end_wait", "number", title: "... for at least this long (min)", required: true
  }

  section ("Send this message") {
    input "message", "text", title: "Notification message", description: "Laudry is done!", required: true
  }

  section ("Notification method") {
    input "sendPushMessage", "bool", title: "Send a push notification?"
  }

  section ("Additionally", hidden: hideOptionsSection(), hideable: true) {
    input "phone", "phone", title: "Send a text message to:", required: false
    input "switches", "capability.switch", title: "Turn on this switch", required:false, multiple:true
    input "hues", "capability.colorControl", title: "Turn these hue bulbs", required:false, multiple:true
    input "color", "enum", title: "This color", required: false, multiple:false, options: ["White", "Red","Green","Blue","Yellow","Orange","Purple","Pink"]
    input "lightLevel", "enum", title: "This light Level", required: false, options: [[10:"10%"],[20:"20%"],[30:"30%"],[40:"40%"],[50:"50%"],[60:"60%"],[70:"70%"],[80:"80%"],[90:"90%"],[100:"100%"]]
    input "speech", "capability.speechSynthesis", title:"Speak message via: ", multiple: true, required: false
  }
}

def installed() {
  log.debug "Installed with settings: ${settings}"

  initialize()
}

def updated() {
  log.debug "Updated with settings: ${settings}"

  unsubscribe()
  initialize()
}

def initialize() {
  subscribe(acceleration, "acceleration", handler)
}

def handler(evt) {
	def delay = cycle_end_wait * 60

    if (!state.cycleOn && acceleration.latestValue("acceleration") == "active") {
        cycleOn(evt)
    }
    // If power drops below threshold, wait for a few minutes.
    else if (state.cycleOn && acceleration.latestValue("acceleration") == "inactive") {
        runIn(delay, "cycleOff")
    }
}

private cycleOn(evc) {
    state.cycleOn = true
    state.cycleStart = now()
    log.trace "Cycle started."
}

private cycleOff() {

    // If power is still below threshold, end cycle.
    if (state.cycleOn && acceleration.latestValue("acceleration") == "inactive") {
        state.cycleOn = false
        state.cycleEnd = now()
		speechAlert(message)
        send(message)
        lightAlert(evt)
    }
}

private lightAlert(evt) {
  def hueColor = 0
  def saturation = 100

  if (hues) {
      switch(color) {
          case "White":
              hueColor = 52
              saturation = 19
              break;
          case "Daylight":
              hueColor = 53
              saturation = 91
              break;
          case "Soft White":
              hueColor = 23
              saturation = 56
              break;
          case "Warm White":
              hueColor = 20
              saturation = 80 //83
              break;
          case "Blue":
              hueColor = 70
              break;
          case "Green":
              hueColor = 39
              break;
          case "Yellow":
              hueColor = 25
              break;
          case "Orange":
              hueColor = 10
              break;
          case "Purple":
              hueColor = 75
              break;
          case "Pink":
              hueColor = 83
              break;
          case "Red":
              hueColor = 100
              break;
      }

      state.previous = [:]

      hues.each {
          state.previous[it.id] = [
              "switch": it.currentValue("switch"),
              "level" : it.currentValue("level"),
              "hue": it.currentValue("hue"),
              "saturation": it.currentValue("saturation")
          ]
      }

      log.debug "current values = $state.previous"

      def newValue = [hue: hueColor, saturation: saturation, level: lightLevel as Integer ?: 100]
      log.debug "new value = $newValue"

      if (switches) {
          switches*.on()
      }
      hues*.setColor(newValue)
  }
}

private speechAlert(msg) {
	if (speech){
  		speech.speak(msg)
    }    
}

private send(msg) {
  if (sendPushMessage) {
    sendPush(msg)
  }

  if (phone) {
    sendSms(phone, msg)
  }

  log.debug msg
}

private hideOptionsSection() {
  (phone || switches || hues || color || lightLevel) ? false : true
}
1 Like

I love the community here…thanks so much! I’m trying tonight!!!

1 Like

There is an easy way to trigger speach without vitual switches as well. My Ubi is gathering dust right now, but I’ll see if I can find the code. It works with any app using speech synthesis (including the one above) and requires a simple http request rule to be set up on the ubi portal

1 Like

I was pretty sure this worked with the UBI using the UBI device type speak method using its speechSynthesis cabability? This code wouldn’t require any virtual tiles as i understand it. I don’t have an UBI so i’m not 100% sure… but the code above does not call any sort of virtual tile but rather the speak method in the device code.

As long as you are using the connect app and device here. You should be able to use the code above with no problem at all.

1 Like

@byoung had mentioned a virtual switch (which she could theoretically use in your app) in her original post. I’m not sure if the standard Ubi device type supports speech synthesis, but I know there was a community one floating around here that did. Because it supported other things (like sensors) which I didn’t need, I had put together a device type that only did the speech part.

The more I think about it, I believe the standard Ubi install doesn’t even use a device type, but just a smart app connection, but it’s been awhile.

Ahhhh ok. Understood.

@byoung Make sure you are using the device and connect app here and you should be good.

Loading the app and device type now! Thanks again for the help!

Using the Custom Ubi device type and app by @jody.albritton :Ubi Connect
and the app Big Talker by @rayzurbock: Big Talker
you can get things like motion, switches, presences, locks, contacts, modes, accelerations activities announce over Ubi or Sono’s

2 Likes

This works perfectly! Any way to add the ‘minimum cycle time’ from the Laundry app so that it doesn’t notify me after opening the dryer door? Ie. there has to be vibration for a minimum of 5 minutes and when it has stopped for 10 minutes trigger notification. Right now it tells me when the dryer has stopped, and then again after I have closed the dryer door (as it detected vibration again and then it stops).

I can see the preference for this but am not sure how to add it to the code below.

section(“Time thresholds (in minutes, optional)”){
input “cycleTime”, “decimal”, title: “Minimum cycle time”, required: false, defaultValue: 10
input “fillTime”, “decimal”, title: “Time to fill tub”, required: false, defaultValue: 5
}

This is already in there

Must be something I’m doing then. It keeps notifying after the laundry is out. I’ll check the logs.

It will notify you every time it senses vibration then X amount later vibration stops. If you close the door for instance that can be detected as vibration depending on the positioning of your sensor…If you walk on the floor near it and it senses that vibration… etc. Only way to get around this is to work on the positioning of your sensor.

would it be possible to set it up to say vibrations of 15 mins are a baseline?

1 Like