Some useful wee bits of code from my Node-red datalogger

Folks may find this interesting and usefull

Uses MakerAPI

Grabs all device details periodically of when poked from HE via

As an aside I would really like a Rule Machine trate limiting option. I manage this currently as shown above with a cancelable timer. Does anyone have a better idea?
Also how about allowing a response from an HTTP request to be assigned to a variable in RM.

var msg1 = {};
var devs = [];

var a = {};

var obj = {};
var names = [];
var values = [];

for(j = 0;j < msg.payload.length;j++)
{
    
    var label = msg.payload[j].label;
    var name = msg.payload[j].name;
    var id = msg.payload[j].id;
    a  = msg.payload[j].attributes;
    
    var dev = {};
    dev.id = id;
    dev.label = label;
    dev.name = name;
    dev.attributes = a;
    devs.push( dev );
    

}

msg1.payload = devs;


return msg1;

Then a function for each device for selecting data

image



var msg1 = {};

var devicelabel = "Outside Heat Index";

msg1.topic = devicelabel;

var obj = {};
var names = [];
var values = [];

for(j = 0;j < msg.payload.length;j++)
{
    //node.warn( msg.payload[j]);
    
    if( msg.payload[j].label == devicelabel)
    {
        obj = msg.payload[j];
        
        //node.warn(obj);
        
        var a = obj.attributes;
        
        for(i = 0;i < a.length;i++)
        {
            var val = a[i];
        
            obj[val.name] = (val.currentValue == 999 ? 0 :  val.currentValue);
            
            
        }
        
        msg1.payload = obj.attributes;
        msg1.payload.devicelabel = devicelabel;
       
        break;
    }

}


//node.warn(obj);

return msg1;

I have a mix of Shelly plus various other Arduino, Zigbee Xiaomi and Sonoff devices. No Zwave so far, quite a few polled LAN based Arduino and Shelly devs.

And a wee bit of Shelly integration with my own drivers.

Here is my Shelly Plus 1 PM driver. Very simple but reliable devices just what I needed as I'm not wanting a Tamagotchi home automation system, just a working system with LAN bases access where possible.

import groovy.json.JsonSlurper

metadata {
    definition(name: "Shelly Plus 1PM", namespace: "Greenway", author: "Nick Goodey") {
        capability "Switch"
        capability "Sensor"
        capability "Initialize"
        capability "Polling"
        capability "PowerMeter"
        capability "Switch Level"
        capability "ChangeLevel"
        
        command "ClearStates" // Clear all device states
        command "on"
        command "off"
        
        attribute "temperature", "number"
        attribute "power", "number"
        attribute "current", "number"
  

        attribute "TotalHours", "number"
        attribute "OnHours", "number"
        attribute "DutyCycle", "number"
        attribute "TotalPower", "number"
        attribute "TotalkWh", "number"
        attribute "PrevDaykWh", "number"
        attribute "ConsumptionTile", "string"
 
    }
    
}

preferences {
    
    
    section("Device") {
         input name: "UpdateSeconds", type: "number", title: "Update Seconds", defaultValue: "6", description: "Sampling rate in seconds", required: true, displayDuringSetup: true
         input name: "MinPower", type: "number", title: "Power kW OFF", defaultValue: "0.01", description: "Power level considered as OFF", required: true, displayDuringSetup: true
    }
    
    section("URIs") {
        input "DeviceIP", "text", title: "Shellly device IP address", required: false
        input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: true
    }
    

}





def initialize() {

    log.info "updated..."
    
    log.warn "debug logging is: ${logEnable == true}"
    
    if (logEnable) runIn(1800, logsOff)   
     
    
    def now = new Date().getTime()
    state.stateChangeTime = now
    
    unschedule()
    
    if(UpdateSeconds > 0)
         runIn(UpdateSeconds,  GetStatus)
    
    schedule("0/59 * * * * ? *", UseCounter)
    schedule("0 1 0 1/1 * ? *", ZeroDailyCounters)
}


def ClearStates() {
    
    log.debug("ClearStates(): Clearing device states")
    state.clear()
    ZeroDailyCounters()
}


def setLevel(value, rate = null) {
    
    sendEvent(name: "level", value: value)
    
}

def ZeroDailyCounters() {
    
    log.debug "ZeroDailyCounters"
    
    state.PrevDaykWh = state.kWh
  
    state.onmilis = 0
    state.totalmilis = 0
    state.lasttick = now()
    state.kWh = 0
    state.pow = 0
    state.lastpow = 0
                
    
    state.Lasttotalhours = 0
    state.Lastonhours = 0
    state.Lastdutycycle = 0
    state.LastkWh = 0
    state.LastkWh2 = 0
    
    unschedule()
    
    UseCounter()
	
	poll()

    schedule("0/59 * * * * ? *", UseCounter)
    schedule("0 1 0 1/1 * ? *", ZeroDailyCounters)

}




def UseCounter()
{
    try{

		if (logEnable) log.debug "UseCounter"

		if(state.status == null) state.status = 0
		if(state.onmilis == null) state.onmilis = 0
		if(state.totalmilis == null) state.totalmilis = 0
		if(state.lasttick == null) state.lasttick = now()
		if(state.kWh == null) state.kWh = 0
        if(state.TotalkWh == null) state.TotalkWh = 0
		
        if(state.Lasttotalhours == null)  state.Lasttotalhours = 0
		if(state.Lastonhours == null)  state.Lastonhours = 0
		if(state.Lastdutycycle == null)  state.Lastdutycycle = 0
		
        if(state.LastkWh == null)  state.LastkWh = 0
		if(state.LastkWh2 == null)  state.LastkWh2 = 0
		if(state.LastTotalkWh == null) state.LastTotalkWh = 0
        if(state.lasttotalpower == null) state.lasttotalpower = 0
        
		def elapsedmilis = (now() - state.lasttick)
		
		if (logEnable) log.debug "elapsedmilis = $elapsedmilis"
		
		state.totalmilis += elapsedmilis   
		MinPower=0.1
		if( (state.status > 0) && (state.pow > MinPower) ) state.onmilis += elapsedmilis
		
		def deltaT = (now() - state.lasttick) / 3600000

		state.lasttick = now()
		
		//Double.parseDouble().round(2)    
		state.dutycycle =  (state.onmilis / (state.totalmilis > 0 ? state.totalmilis : 1)) * 100   
		
	   if (logEnable) log.debug "state.totalmilis = $state.totalmilis"
	   if (logEnable) log.debug "state.onmilis = $state.onmilis"
	   if (logEnable) log.debug "state.dutycycle = $state.dutycycle"
		
		totalhours = Double.parseDouble("${state.totalmilis / 3600000}")
		onhours = Double.parseDouble("${state.onmilis / 3600000}")
		
		if(state.lasttotalhours != totalhours) sendEvent(name: "TotalHours", value: "${DecimalToHrsMins(totalhours)}", isStateChange: true)
		if( state.lastonhours != onhours) sendEvent(name: "OnHours", value: "${DecimalToHrsMins(onhours)}", isStateChange: true)
		if(state.lastdutycycle != Double.parseDouble("$state.dutycycle").round(2)) sendEvent(name: "DutyCycle", value: "${Double.parseDouble("$state.dutycycle").round(2)}", isStateChange: true)
			
		state.lasttotalhours = totalhours
		state.lastonhours = onhours
		state.lastdutycycle = Double.parseDouble("$state.dutycycle").round(2)
			
		state.kWh += (deltaT > 0 ? state.pow * deltaT : 0)
        state.TotalkWh += (deltaT > 0 ? state.pow * deltaT : 0)
		
		if (logEnable) log.info "state.pow=$state.pow   dT=$deltaT   kWh=$state.kWh"
		
		if(state.lasttotalpower != Double.parseDouble("$state.kWh").round(3)) sendEvent(name: "TotalPower", value: "${Double.parseDouble("$state.kWh").round(3)}", isStateChange: true)			
		state.lasttotalpower = Double.parseDouble("$state.kWh").round(3)
                               
                      
		if(state.LastTotalkWh != Double.parseDouble("$state.TotalkWh").round(1)) sendEvent(name: "TotalkWh", value: "${Double.parseDouble("$state.TotalkWh").round(1)}", isStateChange: true)			
		state.LastTotalkWh = Double.parseDouble("$state.TotalkWh").round(1)
        
        if(state.PrevDaykWh == null) state.PrevDaykWh = state.kWh
        
        sendEvent(name: "PrevDaykWh", value: "${Double.parseDouble("$state.PrevDaykWh").round(1)}", isStateChange: true)
        
        //ConsumptionTile        
        perHour = state.kWh / (totalhours > 0 ? totalhours : 1)
        
        mu = "<span style='font-size:0.8em'>Total Hours ${DecimalToHrsMins(totalhours)}<br>On Hours ${DecimalToHrsMins(onhours)}<br>On % ${Double.parseDouble("$state.dutycycle").round(2)}<br>kWh ${Double.parseDouble("$state.kWh").round(3)}<br>kWh/hours ${Double.parseDouble("$perHour").round(3)}<br></span>"
        
        sendEvent(name: "ConsumptionTile", value: "$mu", isStateChange: true)
                 
    } 
	catch (Exception e) 
	{
		log.warn "UseCounter() ${e.message}"
	}
    
}

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

def updated() {
    
   initialize();  
    
}

def poll() {
             
    if (logEnable) UseCounter()
    
    runIn((int)getRandomNumber(0, 5),  GetStatus)
}

def myCallbackMethod(response, data) {
    
	
    try{
    
		
		if(data["dataitem1"] == "datavalue1"){
			
			if (logEnable) log.debug "status of get call is: ${response.status}"
			
			json = new groovy.json.JsonSlurper().parseText("${response.getData()}")
		
			if (logEnable) log.debug "data was parsed successfully"
        
			if( json != null) {
				
				/*
				{
					"id":0,
					"source":"HTTP",
					"output":false,
					"apower":0.0,
					"voltage":233.9,
					"current":0.000,
					"aenergy":{
						"total":115.688,
						"by_minute":[
							1024.371,
							0.000,
							0.000
						],
					"minute_ts":1654739159
					},
					"temperature":{
						"tC":58.4,
						"tF":137.2
					}
				}
				*/
				
				if (logEnable) {
					 log.debug json

				}
				
				/*
				attribute "Tempearture", "number"
				attribute "Power", "number"
				attribute "TotalEnergy", "number"
				attribute "TotalCost", "string"
				*/
                
                //log.debug "######   $json.output     state.status=$state.status"
				
				if((json.output == true) && (state.status != 1))
                {
                    sendEvent(name: "switch", value: "on", isStateChange: true)
                    state.status = 1;
                    state.DeviceMode = "ON"
                    
                }
                else if((json.output == false) && (state.status != 0))
                {
                    sendEvent(name: "switch", value: "off", isStateChange: true)
                    state.status = 0;
                    state.DeviceMode = "OFF"
                    
                }
                
				amps = Double.parseDouble("$json.current")          
				state.pow = Double.parseDouble("$json.apower") / 1000.0
                
				
                if(state.lastTemp == null) state.lastTemp = 0
                if(state.lastpow == null) state.lastpow = 0
                if(state.lastamps == null) state.lastamps = 0
                
				if(state.lastTemp != Double.parseDouble("$json.temperature.tC").round(1)) sendEvent(name: "temperature", value: "${Double.parseDouble("$json.temperature.tC").round(1)}")                
                state.lastTemp = Double.parseDouble("$json.temperature.tC").round(1)
                
				if(state.lastpow != Double.parseDouble("$state.pow").round(3)) sendEvent(name: "power", value: "${Double.parseDouble("$state.pow").round(3)}")
               
                state.lastpow = Double.parseDouble("$state.pow").round(3)
				
                                
                if(state.lastamps != Double.parseDouble("$amps").round(3)) sendEvent(name: "current", value: "${Double.parseDouble("$amps").round(3)}")
                state.lastamps = Double.parseDouble("$amps").round(3)
               
                
				if(UpdateSeconds > 0) runIn((int)getRandomNumber(UpdateSeconds, 5),  GetStatus)
                
			}        
            
		}	
	
	} 
	catch (Exception e) 
	{
		log.warn "Callback ${e.message}"
		if(UpdateSeconds > 0) runIn(UpdateSeconds,  GetStatus)
	}
}

def getRandomNumber(min, range){

    double num = ((double)min + (Math.random() * (double)range))
    
    return Double.parseDouble("$num").round(2)
}

def HTTPwebhook(uri) {
    
	if (logEnable) log.debug "GET request $uri"

	try {
		
		httpGet(uri) { resp ->
			if (resp.success) {
				
				sendEvent(name: "WebhookResponse", value:"${resp.data}", isStateChange: true)

			}
			if (logEnable)
				if (resp.data) log.debug "${resp.data}"
		}
	} 
	catch (Exception e) 
	{
		log.warn "Call to on failed: ${e.message}"
	}
    
}

def GetStatus()
{
  
        url = "http://${settings.DeviceIP}/rpc/Switch.GetStatus?id=0"
     
        if (logEnable) log.debug url
         
        def postParams = [
		        uri: url,
		        requestContentType: 'application/json',
		        contentType: 'application/json',
		        headers: ['CustomHeader':'CustomHeaderValue'],
		        body : ["name": "value"]
	         ]
         
        asynchttpGet('myCallbackMethod', postParams, [dataitem1: "datavalue1"])
        
}





def DecimalToHrsMins(Double dhours)
{
    hours = (dhours != null ? dhours.toInteger() : 0);
   
    Integer mins = (Double.parseDouble("$hours") != null ? ((dhours - Double.parseDouble("$hours")) * 60).toInteger() : 0);
   
    if(("$mins").length() < 2) smins = "0$mins"; else smins = "$mins";
    
    hoursMins = "$hours:$smins";
    
     if (logEnable) log.debug " DecimalToHrsMins($dhours) = $hoursMins"
    
    return "$hoursMins";
}

def parse(String description) {
    if (logEnable) log.debug(description)
}

def on() {
    
    HTTPon()
}

def off() {
    
   HTTPoff()
    
}



def HTTPon() {
    
    uri = "http://${settings.DeviceIP}/rpc/Switch.Set?id=0&on=true"
    
    if (logEnable) log.debug "GET request $uri"

    

        //http://192.168.1.40/rpc/Switch.Set?id=0&on=true
        try {
            
            httpGet(uri) { resp ->
            if (resp.success) {
                sendEvent(name: "switch", value: "on", isStateChange: true)
                state.status = 1
                state.DeviceMode = "ON"
                UseCounter()
               if(UpdateSeconds > 0) runIn(3,  GetStatus)
            }
            if (logEnable)
                if (resp.data) log.debug "${resp.data}"
        }
        } catch (Exception e) {
            log.warn "Call to on failed: ${e.message}"
        }
    
}

def HTTPoff() {
    
    uri = "http://${settings.DeviceIP}/rpc/Switch.Set?id=0&on=false"
    
    if (logEnable) log.debug "GET request $uri"
  
        
        try {
            httpGet(uri) { resp ->
            if (resp.success) {
                sendEvent(name: "switch", value: "off", isStateChange: true)
                state.status = 0;
                state.DeviceMode = "OFF"
                UseCounter()
                if(UpdateSeconds > 0) runIn(3,  GetStatus)
            }
            if (logEnable)
                if (resp.data) log.debug "${resp.data}"
        }
        } catch (Exception e) {
            log.warn "Call to off failed: ${e.message}"
        }
    
}


2 Likes

May want to check to see what is in %value% and %text% - I seem to recall some discussion that leads me to believe that they may be used…

I'll look into it

image

The response

Download the Hubitat app