Securifi Peanut Smart Plug

Thanks, the complete fingerprint is only visible whilst on the device join page by selecting the more button.
Please paste the text, that makes it easier for me to update the driver.

2 Likes

How about this? ID: 91CF

Manufacturer: Securifi Ltd.�W�#�Y�

Product Name:

Model Number:

deviceTypeId: 125

more...

manufacturer : Securifi Ltd.�W�# �Y�

idAsInt : 1

inClusters : 0000,0001,0003,0004,0005,0006,0B04,0B05

endpointId : 01

profileId : 0104

application :

outClusters : 0000,0001,0003,0004,0005,0006,0019,0B04,0B05

initialized : true

model :

stage : 4

Sorry I got ahead of myself with the screenshot.....reread and corrected

4 Likes

WooHoo, Thanks Mike.
This plug is an excellent reapeater. Any chance to get the power reporting working? Several other guys tried to create a driver unsuccessfully.
Here's the ST driver

If it doesn't report with the generic outlet driver, not sure what's wrong with it.

Are you saying the st driver doesn't report power either?

with HE it reports 0, even after running a space heater for a few minutes with reporting set to 1 watt increments.
When it was paired to ST the power reports were all over the place, negative numbers. i think it wasn't being parsed correctly.
If it's helpful I can pair back to ST and report, but as I recall without the driver above there wasn't any power reports, and with the driver reports were crazy.
NOTE my plugs have the latest firmware, as without it zero power reports

I've been slowly working on one, using Steve White's Iris Power Plug as a basis. I think the "power" value is reading correctly, but the voltage value displayed is way off (I have not bothered to work on it yet though). I have been trying to work an "update" mechanism into the driver so people can keep the latest driver on hand (per This Thread).

The fingerprint I am using is:
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0009,0B04,0B05", outClusters: "0019", manufacturer: "Securifi Ltd.Gd�# /f�", model: "outlet", deviceJoinName: "Outlet"

what about the rest of the out clusters? they seem missing from your fingerprint, no?

Not all devices have out clusters

Were you able to get the power reporting to work with these zigbee plugs? Is power reporting contingent on a specific firmware version?

You have to update the firmware to the latest version with an Almond router. It seems crazy that companies never update the shipping firmware version. If you want power reporting your better off getting a Samsung plug. They are excellent repeaters and I’ve never had one drop off the network.

1 Like

FYI, just a heads up even with the updated firmware you will get current "power" reporting but "energy monitoring" and accumulation still doesn't exist.

2 Likes

Except that I hate Samsung with a passion

1 Like

I understand. I'm not a real big fan myself, but the darn things work so well. They consistently switch on and off faster than my peanut plugs, never a delay and never go unresponsive. They appear to route for at least 5 devices, since that is how many I've seen associated with one in the zigbee routing table at the same time.

1 Like

Getting this error when trying to install this driver.

unable to resolve class physicalgraph.zigbee.zcl.DataType @ line 32, column 1. unable to resolve class physicalgraph.device.HubAction @ line 217, column 28.

1 Like

That's a SmartThings device handler, not a Hubitat driver. If you have Peanut plugs whose firmware has been updated to support power reporting, then this driver works with Hubitat:

/**
 *  Peanut Plug
 *
 *  Copyright 2017 pakmanw@sbcglobal.net
 *
 *  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.
 *
 *  Peanut Plug
 *
 *  Author: pakmanw@sbcglobal.net
 *
 *  Change Log
 *  2017-09-17 - v01.01 Created
 *  2018-03-01 - v01.02 fix power accuracy issue
 *  2018-12-23 - v01.03 merging jamesham change to get the calibrated attr from peanut plug,
 *                      add support for new smartthings app
 *  2019-01-17 - v01.04 merging jamesham retain state code
 *  2019-05-24 - V02.00 Converted to run on Hubitat.  Modified to display power correctly.
 *  2019-06-14 - V02.50 Added user variables Power Change Report Value, Power Reporting Interval,
 *                      Current Change Report Value, Current Reporting Interval, Voltage Reporting Interval, and
 *                      Debug Logging?
 */

import hubitat.zigbee.zcl.DataType

metadata {
	definition (name: "Peanut Plug", namespace: "pakmanwg", author: "pakmanw@sbcglobal.net", ocfDeviceType: "oic.d.switch",
		vid: "generic-switch-power-energy") {
		capability "Energy Meter"
		capability "Actuator"
		capability "Switch"
		capability "Power Meter"
		capability "Polling"
		capability "Refresh"
		capability "Configuration"
		capability "Sensor"
		capability "Light"
		capability "Health Check"
		capability "Voltage Measurement"
        
		attribute "current","number"

		command "reset"
       
		fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0004, 0005, 0006, 0B04, 0B05",
			outClusters: "0000, 0001, 0003, 0004, 0005, 0006, 0019, 0B04, 0B05"
	}

	// tile definitions
	tiles {
		standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
			state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC"
			state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
		}
		valueTile("power", "device.power") {
			state "default", label:'${currentValue} W'
		}
		valueTile("energy", "device.energy") {
			state "default", label:'${currentValue} kWh'
		}
		valueTile("voltage", "device.voltage") {
			state "default", label:'${currentValue} V'
		}
		valueTile("current", "device.current") {
			state "default", label:'${currentValue} A'
		}
		standardTile("reset", "device.energy", inactiveLabel: false, decoration: "flat") {
			state "default", label:'reset kWh', action:"reset"
		}
		standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") {
			state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
		}

		main(["switch","power","energy","voltage","current"])
		details(["switch","power","energy","voltage","current","refresh","reset"])
	}

	preferences {
        section {
		    input (
                name: "RetainState",
                type: "bool",
                title: "Retain State?",
                description: "Retain state on power loss?", 
                required: false, 
                displayDuringSetup: false, 
                defaultValue: true
            )
i/*		    input (
                name: "PowerReportValueChange",
                type: "enum",
                title: "Power Report Value Change",
                submitOnChange: true,
                options: ["No Selection","No Report",".1 Watt",".2 Watts",".3 Watts",".4 Watts",".5 Watts",
                            "1 Watt","2 Watts","3 Watts","4 Watts","5 Watts","10 Watts","25 Watts",
                            "50 Watts","75 Watts","100 Watts","150 Watts","200 Watts","250 Watts","300 Watts","400 Watts",
                            "500 Watts","750 Watts","1000 Watts"],
                required: true,
                Multiple: false
            )
		    input (
                name: "PowerReportPercentChange",
                type: "enum",
                title: "Power Report Percentage Change",
                submitOnChange: true,
                options: ["No Selection","No Report","1%","2%","3%","4%","5%","10%","15%","20","25%","30%","40%","50%","75%","100%"],
                required: true,
                Multiple: false
            )
		    input (
                name: "PowerReportingInterval",
                type: "enum",
                title: "Power Reporting Interval",
                submitOnChange: true,
                options: ["No Selection","No Report","5 Seconds","10 Seconds","15 Seconds","30 Seconds","45 Seconds","1 Minute",
                            "2 Minutes","3 Minutes","4 Minutes","5 Minutes","10 Minutes","15 Minutes","30 Minutes","45 Minutes",
                            "1 Hour","2 Hours","3 Hours","5 Hours"],
                required: true,
                Multiple: false
            ) */
		    input (
                name: "ReportablePowerChange",
                type: "number",
                title: "Power Change Report Value",
                description: "Report Power change greater than XXX watts. (.1 - 1000)",
                submitOnChange: true,
                required: true,
                range: "0..1000",
                defaultValue: 5
            )
		    input (
                name: "MinPowerReportTime",
                type: "number",
                title: "Power Reporting Interval",
                description: "Report Power no more than every XXX seconds. (1 - 7200)",
                submitOnChange: true,
                required: true,
                range: "1..7200",
                defaultValue: 60
            )
		    input (
                name: "ReportableCurrentChange",
                type: "number",
                title: "Current Change Report Value",
                description: "Report Current change greater than XXX milliamps. (1 - 1000)",
                submitOnChange: true,
                required: true,
                range: "1..1000",
                defaultValue: 1
            )
		    input (
                name: "MinCurrentReportTime",
                type: "number",
                title: "Current Reporting Interval",
                description: "Report Current no more than every XXX seconds. (1 - 7200)",
                submitOnChange: true,
                required: true,
                range: "1..7200",
                defaultValue: 60
            )
		    input (
                name: "MinVoltageReportTime",
                type: "number",
                title: "Voltage Reporting Interval",
                description: "Report Voltage no more than every XXX seconds. (1 - 7200)",
                submitOnChange: true,
                required: true,
                range: "1..7200",
                defaultValue: 60
            )
		    input (
                name: "DebugLogging",
                type: "bool",
                title: "Debug Logging",
//                description: "Enable Debug Logging", 
                required: true, 
                displayDuringSetup: false, 
                defaultValue: true
            )
        }
	}
}

def log(msg) {
	if (DebugLogging) {
		log.debug(msg)	
	}
}

// Parse incoming device messages to generate events
def parse(String description) {

//	zigbee.ELECTRICAL_MEASUREMENT_CLUSTER is 2820
	log "description is: $description"
	def event = zigbee.getEvent(description)
	if (event) {
	    log "event name is $event.name"
		if (event.name == "power") {
			def powerValue
			powerValue = (event.value as Integer) * getPowerMultiplier()
			sendEvent(name: "power", value: powerValue)
			def time = (now() - state.time) / 3600000 / 1000
			state.time = now()
			log "powerValues is $state.powerValue"
			state.energyValue = state.energyValue + (time * state.powerValue)
			state.powerValue = powerValue
			// log "energyValue is $state.energyValue"
			sendEvent(name: "energy", value: state.energyValue)
		} else {
			sendEvent(event)
		}
	} else if (description?.startsWith("read attr -")) {
		def descMap = zigbee.parseDescriptionAsMap(description)
		log "Desc Map: $descMap"
		if (descMap.clusterInt == zigbee.ELECTRICAL_MEASUREMENT_CLUSTER) {
			def intVal = Integer.parseInt(descMap.value,16)
			if (descMap.attrInt == 0x0600) {
				log.info "ACVoltageMultiplier $intVal"
				state.voltageMultiplier = intVal
			} else if (descMap.attrInt == 0x0601) {
				log.info "ACVoltageDivisor $intVal"
				state.voltageDivisor = intVal
			} else if (descMap.attrInt == 0x0602) {
				log.info "ACCurrentMultiplier $intVal"
				state.currentMultiplier = intVal
			} else if (descMap.attrInt == 0x0603) {
				log.info "ACCurrentDivisor $intVal"
				state.currentDivisor = intVal
			} else if (descMap.attrInt == 0x0604) {
				log.info "ACPowerMultiplier $intVal"
				state.powerMultiplier = intVal
			} else if (descMap.attrInt == 0x0605) {
				log.info "ACPowerDivisor $intVal"
				state.powerDivisor = intVal
			} else if (descMap.attrInt == 0x0505) {
				def voltageValue = intVal * getVoltageMultiplier()
				log "Voltage ${voltageValue}"
				state.voltage = $voltageValue
				sendEvent(name: "voltage", value: voltageValue)
			} else if (descMap.attrInt == 0x0508) {
				def currentValue = String.format("%.4f", (intVal * getCurrentMultiplier()))
				log "Current ${currentValue}"
				state.current = $currentValue
				sendEvent(name: "current", value: currentValue)
			} else if (descMap.attrInt == 0x050B) {
				def powerValue = String.format("%.4f", (intVal * getPowerMultiplier()))
//				log "Power ${intVal}, ${getPowerMultiplier()}"
				log "Power ${powerValue}"
				state.powerValue = $powerValue
				sendEvent(name: "power", value: powerValue)
			}
		} else {
			log.warn "Not an electrical measurement"
		}
	} else {
		log.warn "DID NOT PARSE MESSAGE for description : $description"
		log zigbee.parseDescriptionAsMap(description)
	}
}

def installed() {
	reset()
	configure()
	refresh()
}

def off() {
	zigbee.off()
}

def on() {
	zigbee.on()
}

def refresh() {
	Integer reportIntervalMinutes = 5
	setRetainState() +
	zigbee.onOffRefresh() +
//	zigbee.simpleMeteringPowerRefresh() +
//	simpleMeteringPowerRefresh() +
	zigbee.electricMeasurementPowerRefresh() +
	zigbee.onOffConfig(0, reportIntervalMinutes * 60) +
//	zigbee.simpleMeteringPowerConfig() +
//	simpleMeteringPowerConfig() +
//	zigbee.electricMeasurementPowerConfig() +
	electricMeasurementPowerConfig() +
	voltageMeasurementRefresh() +
	voltageMeasurementConfig() +
	currentMeasurementRefresh() +
	currentMeasurementConfig() +
	zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 0x0600) +
	zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 0x0601) +
	zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 0x0602) +
	zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 0x0603) +
	zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 0x0604) +
	zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 0x0605)
}

//def electricMeasurementPowerConfig(
//                                minReportTime=10,           // in seconds
//                                maxReportTime=600,          // in seconds
//                                reportableChange=0x0005)    // in .1 Watts 
def electricMeasurementPowerConfig()    // in .1 Watts 
{
    def MinPowerValueA
    MinPowerValue = (((ReportablePowerChange as float) * 10) as Integer)
	log.info "Power Report Time: $MinPowerReportTime, Power Report Value: $MinPowerValue"
	zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 
                            0x050B, 
                            DataType.INT16, 
                            MinPowerReportTime as Integer,              // Min Power reporting time in seconds
                            7200,                                       // Max Power reporting time in seconds
                            MinPowerValue as Integer)                   // Min Reportable Power Change in Tenths of Watts
}

//def currentMeasurementConfig(minReportTime=60, maxReportTime=600, reportableChange=0x0005) {
//	zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 0x0508, DataType.UINT16, minReportTime, maxReportTime, reportableChange)
//}

def currentMeasurementConfig() {
	log.info "Current Report Time: $MinCurrentReportTime, Current Report Value: $ReportableCurrentChange"
	zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 
                            0x0508, 
                            DataType.INT16, 
                            MinCurrentReportTime as Integer,    // Min Current reporting time in seconds
                            7200,                               // Max Current reporting time in seconds
                            ReportableCurrentChange as Integer) // Min Reportable Current Change in Tenths of Watts
}

def currentMeasurementRefresh() {
	zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 0x0508);
}

def voltageMeasurementConfig() {
	log.info "Voltage Report Time: $MinVoltageReportTime"
	zigbee.configureReporting(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 
                            0x0505, 
                            DataType.INT16, 
                            MinVoltageReportTime as Integer,    // Min Voltage reporting time in seconds
                            7200,                               // Max Voltage reporting time in seconds
                            0x0030)                             // Min Reportable Voltage Change in Tenths of Volts
}

def voltageMeasurementRefresh() {
	zigbee.readAttribute(zigbee.ELECTRICAL_MEASUREMENT_CLUSTER, 0x0505);
}

def getCurrentMultiplier() {
	if (state.currentMultiplier && state.currentDivisor) {
		return (state.currentMultiplier / state.currentDivisor)
	} else {
		return 0.001831
	}
}

def getVoltageMultiplier() {
	if (state.voltageMultiplier && state.voltageDivisor) {
		return (state.voltageMultiplier / state.voltageDivisor)
	} else {
		return 0.0045777
	}
}

def getPowerMultiplier() {
	if (state.powerMultiplier && state.powerDivisor) {
		return (state.powerMultiplier / state.powerDivisor)
	} else {
		return 0.277
	}
}

def configure() {
	log "in configure()"
	return configureHealthCheck() + setRetainState()
}

def configureHealthCheck() {
	Integer hcIntervalMinutes = 12
	sendEvent(name: "checkInterval", value: hcIntervalMinutes * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
	return refresh()
}

def updated() {
    log.info "${device.displayName}.updated()"
	log "in updated()"
	// updated() doesn't have it's return value processed as hub commands, so we have to send them explicitly
	def cmds = configureHealthCheck() + setRetainState()
	cmds.each{ sendHubCommand(new hubitat.device.HubAction(it)) }
}

def ping() {
	return zigbee.onOffRefresh()
}

def setRetainState() {
	log "Setting Retain State: $RetainState"
	if (RetainState == null || RetainState) {
		if (RetainState == null) {
			log.warn "RetainState is null, defaulting to 'true' behavior"
		}
		return zigbee.writeAttribute(0x0003, 0x0000, DataType.UINT16, 0x0000)
	} else {
		return zigbee.writeAttribute(0x0003, 0x0000, DataType.UINT16, 0x1111)
	}
}

def reset() {
	log "Reset"
	state.energyValue = 0.0
	state.powerValue = 0.0
	state.voltage = 0.0
	state.current = 0.0
	state.time = now()
	sendEvent(name: "energy", value: state.energyValue)
}
3 Likes

I have a couple of these and I’m trying to find the current firmware version. Where can I find that? Which version is required for power reporting to work?

You need to have a securifi router to update them.

1 Like

Has to be reported somewhere (how else could an Almond router know the Peanut Plug needs upgrade) but the drivers I have seen do not list it.

Have not done it yet in the one I am working on... Got that one to be "good enough" for my own needs and have been working on my other drivers and Christmas stuff so it has been put on the back burner.

Even after updating them with the Securifi router, it enables "power" monitoring but it does not enable "Energy" monitoring......just an FYI.

1 Like

Yeah, I am not really clear on the difference between the two. I could be wrong but I think Power is "at this moment" and Energy is "over X timeframe". Not sure if the DO energy or if the Almonds calculated it from the reported Power readings.