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
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}"
}
}