[DRIVER] NUT Discovery + UPS & Outlet Status

Yes it works just fine. As long as you have the right ip address and credentials for your Nut Server it should work. What platform is your Nut Server running on?

1 Like

Raspberry PI 4B 8G running standard raspi debian. I am very new to the linux world. Actually not new, old. It has been many years since i worked Linux/unix. I likley have something setup wrong on the server. though it all seems to work when connect to the RPi.

Standard questions here:

When you run the command below on the PI do you get the UPS current values?

upsc "insert name of UPS from ups.conf"

If you do then you need to validate that nut is configured as a server. What is the mode set to in Nut.conf? MODE needs to be netserver

Is the username password correctly defined the the upsd.users files for remote access?

1 Like

There is an attribute called batteryruntimesec, is it possible to change that to minutes?

Divide by 60.

Of course. But where?

@ronv42

Yes

Yes. netserver

I have the same upsmon user set up in upsd.users and in upsd.conf. Should the on in upsd.users be set up as slave? I have:

[monuser]
password = secret
upsmon master

I also have an admin set up in upsd.users. I have tried that in this device on Hubitat and it doesn't work either. That looks like:
[admin]
password = also_secret
actions = SET
instcmds = ALL

Will special characters in the password cause issues? do i need to escape underscores and exclamation points and parens?

It also troubles me that when I save parameters in this driver, or when I hit initialize after saving params, each save, or init starts another thread of trying to log on. If I hit init 3 times after saving params I have 4 attempts at connecting in the Hubitat log, trying each every 10 secs. The only way to kill these constant attempts is to reboot the Hubitat. There should be some graceful end to this if the connect attempt fails.

Also, I know I read earlier in this thread that this driver simply uses an ip address call to port 3493 and does not use telnet. But the Hubitat logs do say the driver is trying to open telnet. Does that mean I need to have a telnet server on the Raspberry with an ID set up to match the usrmon ID of the NUT server? Of telnet is used can this app be adapted to use ssh instead?

LJ

The port that NUT opens when setup as a NETSERVER using a Telnet protocol but you don't need a telnet server on the PI from what I understand it's built unto the NUTSERVER. When I set up NUT Servers on PI's I usually setup three sets of user credentials:

[upsmaster]
        password = masterpass123
        upsmon master
[upsslave]
        password = slavepass123
        upsmon slave
[upsadmin]
        password = upspass
        actions = SET
        instcmds = ALL

The remote device such as the Hubitat uses the [upsslave] section to log in.

In the upsd.conf file have you made sure that it's allowed to listen on the network adapter address? By default NUT will only listen on 127.0.0.1 3493 you will need to add another line to have the file for example I added 192.168.1.10 3493 to it.

# =======================================================================
# LISTEN <address> [<port>]
# LISTEN 127.0.0.1 3493
# LISTEN ::1 3493
LISTEN 127.0.0.1 3493
LISTEN 192.168.1.10 3493
1 Like

SUCCESS! Thanks so much @ronv42! I was missing the LISTEN settings. This is great. this will do just what I want it to do.

This Hubitat community is the best because of people like you.

Much appreciated,

LJ

1 Like

OK, So my Cyberpower ups works just fine, even though I don't see outlets. Perhaps it doesn't support that.

However my APC ups is not working right. The parent downloads the settings properly, but the child can't parse the majority of the messages. Anybody have any ideas on what going on?

LJ

I would say if you look at the source code the driver is only using "important" data values on the Hubitat. There are just too many UPS's and combinations of attributes/values to maintain on the hub. When I run Debug mode I have many that aren't mapped into hubitat attributes. For myself all I really care about if it's on mains or battery to begin a shutdown along with battery level.

image

metadata {
    definition (name: "NUT Child UPS", namespace: "guyee", author: "Peter Gulyas") {
        capability "Refresh"
		capability "TemperatureMeasurement"
		capability "PowerSource"
		
		// Outlet capabilities
		capability "VoltageMeasurement"		
		capability "PowerMeter"
		capability "Battery"
	}
	
	attribute "batteryRuntimeSecs", "Number"
	attribute "batteryType", "String"
	attribute "batteryVoltage", "Number"

	attribute "deviceManufacturer", "String"
	attribute "deviceModel", "String"
	attribute "deviceType", "String"
	attribute "deviceFirmware", "String"
	attribute "deviceNominalPower", "Number"
	
	attribute "driverName", "String"
	attribute "driverVersion", "String"
	attribute "driverVersionInternal", "String"
	attribute "driverVersionData", "String"
	
	attribute "load", "Number"
	attribute "status", "String"

	attribute "outputVoltage", "Number"
	attribute "outputVoltageNominal", "Number"
	attribute "outputFrequency", "Number"
	attribute "outputFrequencyNominal", "Number"

	attribute "outletDescription", "String"
	attribute "outletSwitchable", "Boolean"
	preferences { 
	input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: true
	}
1 Like

I was just doing that. Yes I can see it is getting most of what is useful. If I want to see others, I guess I can customize them. It looks pretty straightforward. Thanks for the reply.

LJ

OK, So I think I have tried everything and can't figure out how to use the battery, or batteryRuntimeSecs fields as a trigger in an RM rule. My use case is I want a predicate rule that states that if the status is On Battery, I want to trigger a shutdown of Hubitat when the batteryRuntimeSecs reaches 300. Alternatively, I could use Battery such that when the battery gets to 15% I want to shut down Hubitat. Unfortunately I can't seem to be able to access battery or batteryRuntimeSecs using any RM trigger method. I can get PowerSource with value of mains or on battery, but htat seems to be it. Can anyone assist?

LJ

This worked for me using battery level. Ignore the sharptool text line command.

you need to use custom attributes.. here is my rule albeit for my smart ups driver based on runtime..
i have separate attributes for hours and minutes but you get the idea.

1 Like

Don't know how I missed that one. Thanks,

LJ

Here is the code I have been using and seems to work well. I use variables to control the time on UPS vs. checking the battery. What I have found is that even though the UPS reports a long uptime as it running on battery for about 50% of it's rated time it falls off fast that is why I chose 5 minutes to shut it down. If there is no power, no automation is needed and I need the battery to keep the network stack running more than I need the hubitat running.

Anybody figure this out? My log is full of the same messages, coming up every couple of minutes. My Synology NAS is on DSM 6 and I'm monitoring a CyberPower CP1000PFCLCD.

Mine's still doing this. DSM7. Not suer what's up.

OK I don't really understand Groovy or the device driver architecture in Hubitat but I think I figured this out with some old skool printf-style debugging.

First, I noticed that the error message

ParseOK: Couldn't process message: "[]"

disappeared when I turned on Debug Logging in the driver...hmmm. :roll_eyes:

Second, I also noticed that the default (ie, error) choice for the switch() statement in the ParseOK routine was being selected when the device.currentState equaled STATE_CONNECTING.

These two behaviors suggested to me there was a timing problem or race condition around the message parsing and the device state detection, so I added a log.info() message as the first line of the ParseOK() routine, and the execution of this log statement created enough delay that the device.currentState had time to transition from STATE_CONNECTING to STATE_AUTH_PHASE1 or STATE_AUTH_PHASE2, which were the only two choices that the message parser state machine was expecting. Cleaning it up by replacing the log.info() statement with a proper delay loop conditioned on STATE_CONNECTING gave me this revised version of the ParseOK() routine:

def parseOK(String[] msg) {
    int i = 0 
    while ( device.currentState("State", true).value == STATE_CONNECTING ) {
        if( i++ > 1000 ) 
            break ;
    }
    switch(device.currentState("State", true).value) {
		case STATE_AUTH_PHASE1:
			nutAuthPhase2()
			break
		case STATE_AUTH_PHASE2:
			nutListUPS()
			break
		default:
			log.error("ParseOK: Unexpected State is: \"${device.currentState("State", true).value}\", couldn't process message: \"${msg}\"")
	}
}

This has eliminated the error messages from the driver and I don't think it is changing the driver's execution semantics at all.

2 Likes