Started noticing this error from Laundry Manager...
[error]java.lang.NullPointerException: Cannot invoke method currentValue() on null object on line 166 (method resetHandler)
@mark.amber Do you have a dryer selected? That error would result if it can't find the dryer device.
No I do not have a dryer selected (my dryer is 240v electric and I don't have a power monitoring outlet for that
) Since the beginning, I've only had the Washer configured.

I've been having an issue lately (unfortunately I'm not sure exactly when it started) where I get a "dryer has finished" notification almost immediately after starting the dryer. Here is the event log of my Aeotec HD Smart Switch. You can see where it reported 0W when it finished a load yesterday. It reported 4W when I turned it on then jumps up to 6kW once I started the cycle.
Here is the event log for the virtual device the app creates. You see the same thing as above, but for some reason thinks the switch turns off and then back on. I'm not doing anything other than putting clothes in the dryer and starting it.
This is how my preferences look.
I'm using the latest version available in HPM. I do not have a button or contact sensor set in the app.
@mark.amber I had forgotten I made the washer & dryer optional. I need to add an additional check so you don't get the error. Thanks for pointing it out.
@waterboysh If you could please enable logs and PM to me that would help. I have a suspicion of where things might be going wrong, but the logs will help narrow it down.
I'm still having issues with this. Here's what is happening.
I have the washer settings at 8 watts, 60 seconds on, and 60 seconds off. When it's on and not running it's about 3.3 watts. If I open the lid the display wakes up and it goes up to a hair over 6. When I close it the washer is designed to do some kind of prepping or priming of sorts that causes a momentary jump to 20 watts. This jump is far shorter than the 60 second time setting yet it triggers to "running" state. Not only that but it also triggers an announcement that the washer has finished even though it is still in running state. 60 seconds later, as would be normally expected state goes to finished and I get an announcement again. It appears that basically the time setting for on is being ignored.
@markbellkosel84 I've identified an issue where if you're starting from a finished state where it would go from running to finished. The temporary fix is to make sure you reset to idle after every load. I've been super busy lately, but I'm hoping to have a fix soon.
I will check that out tonight. Thank you for the info!
Any progress? I know you've been busy. I'm still getting the errors and just making sure I didn't miss getting the fix. ![]()
I just pushed an update with the following changes:
- Fix for bug related to going from Finished to Idle
- Null check for optional devices
- Added support for a dishwasher
I wanted to split out the reset button for each appliance, so I hacked the below together. Feel free to integrate (or not) in to your build. This is a diff patch, sorry that it caught some differences in my IDE's formatting, etc along with it
EDITED 9/17/2021 -- fixed typo in parameter name:
diff --git a/hubitat-customized-apps/laundry-manager.ORIGINAL.groovy b/hubitat-customized-apps/laundry-manager.ORIGINAL.groovy
index 621d9de0dd4ca026b4b2d15cf460a85c1a98ce41..a0002b4ab945bc4916b201386491a6fd7a3ad27f 100644
--- a/hubitat-customized-apps/laundry-manager.ORIGINAL.groovy
+++ b/hubitat-customized-apps/laundry-manager.ORIGINAL.groovy
@@ -1,7 +1,13 @@
+/* groovylint-disable CompileStatic, DuplicateMapLiteral, DuplicateNumberLiteral, DuplicateStringLiteral, ElseBlockBraces, IfStatementBraces, InsecureRandom, LineLength, MethodCount, MethodReturnTypeRequired, NoJavaUtilDate */
+
+// JD Build v20210902A
+// CHANGELOG:
+// * 20210902A: Split out buttons for washer, dryer, dishwasher
+
/**
* Laundry Manager app for Hubitat
*
- * Copyright (c) 2019 Justin Walker
+ * Copyright (c) 2019-2021 Justin Walker
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
@@ -38,7 +44,7 @@ preferences {
}
def settings() {
- dynamicPage(name: 'settings', title: " ", install: true, uninstall: true) {
+ dynamicPage(name: 'settings', title: ' ', install: true, uninstall: true) {
section('<b>Machines</b><hr>') {
input 'washerMeter', 'capability.powerMeter', title: 'Washer Power Meter'
input 'dryerMeter', 'capability.powerMeter', title: 'Dryer Power Meter'
@@ -46,7 +52,9 @@ def settings() {
}
section('<b>Reset</b><hr>') {
paragraph '<div><i>Automatically reset any finished machine back to idle by subscribing to a button or contact sensor.</i></div>'
- input 'resetButton', 'capability.pushableButton', title: 'Button'
+ input 'resetButton', 'capability.pushableButton', title: 'Washer reset button'
+ input 'resetButtonDryer', 'capability.pushableButton', title: 'Dryer reset button'
+ input 'resetButtonDishwasher', 'capability.pushableButton', title: 'Dishwasher reset Button'
input 'resetContacts', 'capability.contactSensor', title: 'Contact Sensors', multiple: true
input 'resetAuto', 'bool', title: 'Time-based auto reset?', defaultValue: false, submitOnChange: true
if (resetAuto) input 'resetAutoTime', 'number', defaultValue: 60, title: 'Time before resetting (minutes)'
@@ -98,12 +106,14 @@ void init() {
// subscribe to device events
washerMeter && subscribe(washerMeter, 'power', washerPowerHandler)
- dryerMeter && subscribe(dryerMeter, 'power', dryerPowerHandler)
+ dryerMeter && subscribe(dryerMeter, 'power', dryerPowerHandler)
dishwasherMeter && subscribe(dishwasherMeter, 'power', dishwasherPowerHandler)
resetButton && subscribe(resetButton, 'pushed', resetHandler)
+ resetButtonDryer && subscribe(resetButtonDryer, 'pushed', resetHandlerDryer)
+ resetButtonDishwasher && subscribe(resetButtonDishwasher, 'pushed', resetHandlerDishwasher)
resetContacts && subscribe(resetContacts, 'contact', resetHandler)
-
+
createChildren()
}
@@ -113,7 +123,7 @@ void init() {
void createChildren() {
logDebug 'creating Laundry Manager children'
-
+
washerMeter && createChild('laundry-machine-washer', 'Laundry Washer')
dryerMeter && createChild('laundry-machine-dryer', 'Laundry Dryer')
dishwasherMeter && createChild('laundry-machine-dishwasher', 'Dishwasher')
@@ -135,16 +145,15 @@ void createChild(dni, label) {
log.warn 'error creating child device'
log.trace e
}
-
- }
+ }
}
-void subscribeToChild(child){
+void subscribeToChild(child) {
if (child.deviceNetworkId == 'laundry-machine-washer') {
subscribe(child, 'status', washerStatusHandler)
washerPowerHandler()
}
-
+
if (child.deviceNetworkId == 'laundry-machine-dryer') {
subscribe(child, 'status', dryerStatusHandler)
dryerPowerHandler()
@@ -157,7 +166,7 @@ void subscribeToChild(child){
}
void removeChildDevices(delete) {
- delete.each {deleteChildDevice(it.deviceNetworkId)}
+ delete.each { deleteChildDevice(it.deviceNetworkId) }
}
///
@@ -165,29 +174,37 @@ void removeChildDevices(delete) {
///
void resetHandler(evt) {
- logDebug 'button pushed'
-
+ logDebug 'Washer button pushed'
+
def washer = getChildDevice('laundry-machine-washer')
- if(washer?.currentValue('status') == 'finished') {
+ if (washer?.currentValue('status') == 'finished') {
logDebug 'resetting washer'
washer.resetFinished()
}
-
+}
+
+void resetHandlerDryer(evt) {
+ logDebug 'Dryer button pushed'
+
def dryer = getChildDevice('laundry-machine-dryer')
- if(dryer?.currentValue('status') == 'finished') {
+ if (dryer?.currentValue('status') == 'finished') {
logDebug 'resetting dryer'
dryer.resetFinished()
}
+}
+
+void resetHandlerDishwasher(evt) {
+ logDebug 'Dishwasher button pushed'
def dishwasher = getChildDevice('laundry-machine-dishwasher')
- if(dishwasher?.currentValue('status') == 'finished') {
+ if (dishwasher?.currentValue('status') == 'finished') {
logDebug 'resetting dishwasher'
dishwasher.resetFinished()
}
}
void washerStatusHandler(evt) {
- logDebug 'checking washer status'
+ logDebug 'checking washer status'
def washer = getChildDevice('laundry-machine-washer')
// get machine label
@@ -200,7 +217,7 @@ void washerStatusHandler(evt) {
}
void dryerStatusHandler(evt) {
- logDebug 'checking dryer status'
+ logDebug 'checking dryer status'
def dryer = getChildDevice('laundry-machine-dryer')
// get machine label
@@ -208,12 +225,12 @@ void dryerStatusHandler(evt) {
if (labelOverride) {
name = labelDryer ?: name
}
-
+
statusCheck(dryer, name, 'dryerStatusHandler')
}
void dishwasherStatusHandler(evt) {
- logDebug 'checking dishwasher status'
+ logDebug 'checking dishwasher status'
def dishwasher = getChildDevice('laundry-machine-dishwasher')
// get machine label
@@ -221,7 +238,7 @@ void dishwasherStatusHandler(evt) {
if (labelOverride) {
name = labelDishwasher ?: name
}
-
+
statusCheck(dishwasher, name, 'dishwasherStatusHandler')
}
@@ -249,7 +266,7 @@ void dishwasherPowerHandler(evt) {
void disableLogging() {
log.info 'Logging disabled.'
- app?.updateSetting('loggingEnabled',[value:'false',type:'bool'])
+ app?.updateSetting('loggingEnabled', [value:'false', type:'bool'])
}
void logDebug(str) {
@@ -264,8 +281,8 @@ void send(msg) {
// push notifications
Boolean push = sendPushMessage == true || sendPushMessage == 'Yes'
if (push && modeOkay) {
- logDebug 'sending push message'
- notificationDevices.each{ device ->
+ logDebug 'sending push message'
+ notificationDevices.each { device ->
device.deviceNotification(msg)
}
}
@@ -273,23 +290,23 @@ void send(msg) {
// tts devices
if (sendTTS && modeOkay) {
logDebug 'sending TTS message'
- ttsDevices.each{ device ->
+ ttsDevices.each { device ->
device.speak(msg)
}
}
-
+
logDebug msg
}
void statusCheck(device, deviceName, handlerName) {
def status = device.currentValue('status')
-
+
if (status == 'finished') {
// send notification
send("${deviceName} is finished")
// schedule repeat notification
- if(repeat) {
+ if (repeat) {
logDebug 'scheduling a repeat notification'
runIn(repeatInterval * 60, handlerName)
}
@@ -299,4 +316,4 @@ void statusCheck(device, deviceName, handlerName) {
runIn((resetAutoTime ?: 60) * 60, resetHandler)
}
}
-}
\ No newline at end of file
+}
-jd
I have a question. Is there any way in the laundry manager to set the power level at which the washer is considered "OFF"?
When my washer completes the wash portion of the cycle and starts to refill the tub for the rinse cycle, the power drops to 5 watts. Laundry Manager sees that as the load completed so an completion announcement is triggered. We know that the load is not complete, so we just wait for a second announcement indicating the rinse cycle is completed and the load is finished for real this time. I presume if we did a second rinse, that we would have to wait for a third announcement. it would be better if we could just get a single announcement when the power drops to zero and stays there.
@rwclements228 You could set your time off threshold to a larger value… long enough to account for the refill time.
How do you do that?
As far as I can tell, the only user entered parameter is the reset time. I have that set to 20 minutes.
Where can I find the time off threshold? Is that something that has to be midified in the code?
@rwclements228 You do it in the device settings.
Devices > Laundry Washer > Time Threshold (Off)
Thanks so much. I did not realize I could adjust the washer parameters in device settings. I will play around with the settings until it works properly.
Thanks once again. After I adjusted the parameters, the Laundry Manager properly announced the end of the full washer cycle without the intermediate interruption after the wash cycle. You were most helpful.
I updated to the newest version but still get 2 "dryer has finished" notifications almost immediately after starting the dryer.
Do you have an electric dryer or a gas dryer? The power required by an electric dryer is vastly different than for a gas dryer. Take a look at the current readings vs run time for the dryer at various points during the dryer cycle and make sure the settings in the Laundry Dryer device are appropriate for your specific dryer.
I had issues with premature reporting on my washer until I set the Laundry Washer settings appropriately. Now it works well, thanks to Augoisms. 
Apparently I typo'd when naming the parameter vs. what I call it as later. You can wish all you want, but wishes ain't gonna wash your dishes (that rhymed):
- input 'resetButtonDishwisher', 'capability.pushableButton', title: 'Dishwasher reset Button'
+ input 'resetButtonDishwasher', 'capability.pushableButton', title: 'Dishwasher reset Button'
EDIT 9/17/2021: I edited the original post just in case someone uses that and doesn't see this.


