[Release] Tasmota Sonoff Hubitat Driver & Device Support


The iFan driver needs to be converted for this Tasmota firmware. If you are using the one from my Sonoff Connect thread, that is for the firmware posted in that thread.

It wouldn't be that difficult to take the iFan02 driver and make a few modifications so that it will work with this tasmota firmware.

Just to make sure, you can control the iFan03 from the Tasmota web interface right?


@ericm I have created a DH for the iFan03 for Tasmota now but it doesn't work with Sonoff (connect) as of yet. I will post the code after I finish with the final 2 parts of it but may need some assistance in making it work with your diver. I can work the switch with a generic Sonotff Tasmota driver and a separate one now controls the fan part.


@ericm and @jmiele4 Here is my code for the iFan03 the works directly with Tasmota and not through Sonoff (Connect) at this time. There is still some work to be done on the tones when the fan is changed but it works with the fan.

// Hubitat driver for controlling Sonoff iFan03
// Version 1.0.0

metadata {
	definition(name: "Sonoff-Tasmota iFan03", namespace: "damondins", author: "damondins") {
		capability "Actuator"
		capability "Refresh"
        capability "Switch"
        capability "Switch Level"
        //capability "Configuration"

	preferences {		
		section("Sonoff Host") {
            input(name: "ipAddress", type: "string", title: "IP Address", displayDuringSetup: true, required: true)
			input(name: "port", type: "number", title: "Port", displayDuringSetup: true, required: true, defaultValue: 80)

		section("Authentication") {
			input(name: "username", type: "string", title: "Username", displayDuringSetup: false, required: false)
			input(name: "password", type: "password", title: "Password (sent cleartext)", displayDuringSetup: false, required: false)

def parse(String description) {
	def message = parseLanMessage(description)
	def isParsed = false;

	// parse result from current formats
	def resultJson = {}
	if (message?.json) {
		// current json data format
		resultJson = message.json
        log.debug resultJson

	// determine switch state
	if ((resultJson?."FanSpeed" in ["1", 1, "1"])) {
        log.debug "Fanspeed 1"
	else if ((resultJson?."FanSpeed" in ["2", 2, "2"])) {
        log.debug "Fanspeed 2"        
	else if ((resultJson?."FanSpeed" in ["3", 3, "3"])) {
        log.debug "Fanspeed 3"        
	else if ((resultJson?."FanSpeed" in ["0", 0, "0"])) {
        log.debug "Fanspeed off"        
	else {
		log.error "Unable to parse Fan state"

def setSwitchState(Boolean on) {
	sendEvent(name: "switch", value: on ? "on" : "off")

def setFanSpeedState(value) {
	sendEvent(name: "FanLevel", value: "$value")

def setLevel(value, duration=1) {
    log.debug value 
    if (value == 0) {
        sendCommand("BlinkTime%2012%3B%20BlinkCount%201%3B%20delay%2010%3B%20FanSpeed%200%3B%20Power5%203", null)
    else if (value > 0 && value <= 33) {
        sendCommand("BlinkTime%202%3B%20BlinkCount%201%3B%20delay%2010%3B%20FanSpeed%201%3B%20Power5%203", null)
    else if (value > 33 && value <= 66) {
        sendCommand("BlinkTime%202%3B%20BlinkCount%202%3B%20delay%2010%3B%20FanSpeed%202%3B%20Power5%203", null)
    else if (value > 66 && value <= 100) {
        sendCommand("BlinkTime%202%3B%20BlinkCount%203%3B%20delay%2010%3B%20FanSpeed%203%3B%20Power5%203", null)

def push() {
	sendCommand("Power", "Toggle")

def on() {
    sendCommand("BlinkTime%202%3B%20BlinkCount%201%3B%20delay%2010%3B%20FanSpeed%201%3B%20Power5%203", null)
    //sendCommand("Fanspeed", "1")

def off() {
    sendCommand("BlinkTime%2012%3B%20BlinkCount%201%3B%20delay%2010%3B%20FanSpeed%200%3B%20Power5%203", null)
	//sendCommand("Fanspeed", "0")

def poll() {
	sendCommand("FanSpeed", "refresh")

def refresh() {
	sendCommand("FanSpeed", "refresh")

private def sendCommand(String command, payload) {
    sendCommand(command, payload.toString())

private def sendCommand(String command, String payload) {
	log.debug "sendCommand(${command}:${payload}) to device at $ipAddress:$port"

	if (!ipAddress || !port) {
		log.warn "aborting. ip address or port of device not set"
		return null;
	def hosthex = convertIPtoHex(ipAddress)
	def porthex = convertPortToHex(port)

	def path = "/cm"
//	if (payload != null){
//		path += "?cmnd=${command}%20${payload}"
//	}
//	else{
//	if (payload = "refresh"){
//		path += "?cmnd=${command}"
//        log.debug "Run Refresh"
//	}
//	else{
		path += "?cmnd=backlog%20${command}"
//        log.debug "Run Command"
//	}

	if (username){
		path += "&user=${username}"
		if (password){
			path += "&password=${password}"

	def result = new hubitat.device.HubAction(
		method: "GET",
		path: path,
		headers: [
			HOST: "${ipAddress}:${port}"
	return result

private String convertIPtoHex(ipAddress) { 
	String hex = ipAddress.tokenize( '.' ).collect {  String.format( '%02x', it.toInteger() ) }.join()
	return hex

private String convertPortToHex(port) {
	String hexport = port.toString().format('%04x', port.toInteger())
	return hexport


You don't need the connect app for the Tasmota firmware.


Yeah, really the only advantage of my firwmare & the discovery app is that 1) the discovery makes adding the device to Hubitat a little easier & 2) Instant status is supported when you turn the device on with a button or in the case of the iFan02/03 with the remote.


But that is possible without using Sonoff Connect as long as you use the new, Tasmota firmware correct? I am getting instant status updates on my Sonoff basics from local changes on the Sonoff and I don't have Connect installed anymore.


Yep, you are correct, connect just does the discovery and some automatic configuration. The firmware itself and the driver does the instant status updating.

Edit: Also it handles ip address changes if your devices aren't static.


S31 was previouslt reporting correctly. Any idea why the S31 reports significantly different current and power on the Hubitat device page as opposed to the S31 web page. The web view seems to be correct. I deleted the S31 and driver and reinstalled both. It actually went from reporting ridiculously low to absurdly high in Hubitat.

Changing the Module type back to Sonoff Basic and then back to S31 seems to have resolved this.


Not sure. As far as I know it just parses what the device sends to it. Check the logs when the values are off as it should show what the device is sending to see if it matches.


I've got a Sonoff basic switch setup, and for the most part it seems to work. From the Sonoff(Web) console, I can toggle on/off. My goal is to have it turn off after two seconds. On initially creating the device, I can toggle on/off from the Hubitat device screen as well.

However, once I set the Auto Off to 2 seconds, or even revert back to 0 seconds, I can no longer toggle from Hubitat. I can still toggle from Sonoff console however. In the logs, I get the following message for Sonoff (Connect):

app:2302019-08-04 04:46:32.608 pm errorjava.lang.NullPointerException: Cannot get property 'label' on null object on line 69 (configurePDevice)

app:2302019-08-04 04:46:14.780 pm debugCreating Sonoff Wifi Switch with dni: c0a80157:0050

And the following for the Switch:

dev:1942019-08-04 04:51:28.867 pm errorgroovy.lang.MissingMethodException: No signature of method: java.lang.String.encodeAsBase64() is applicable for argument types: () values: [] Possible solutions: decodeBase64() on line 339 (on)

dev:1942019-08-04 04:51:28.806 pm debug/cm?cmnd=Power%20On

dev:1942019-08-04 04:51:28.805 pm debugon()

I'm using the Sonoff Basic, with the Sonoff Wifi Switch driver, S20 from erocm123, on this thread. The Basic was flashed using the Sonoff.Classic firmware also from eromcm123.

Any ideas of what I might have done wrong?

Updated: I don't see the option in the Console app to enable Hubitat/SmartThings, which suggests that I don't have something done correctly. Any ideas where I might have gone wrong?


Is this the firmware you used?:


Also, can you post a screeshot of the web interface for the device?


Actually, no. There were so many links for firmware, and it wasn't clear to me which to use. But I'll give that one a try tomorrow and see where it gets me. Thanks for responding @ericm


@ericm Does the 'minimal' firmware need to be installed first, or can I flash with just the firmware on the link you sent? I'm flashing using a USB to TTL adapter and ESPEasy Flasher, and not OTA.


If you are not doing OTA, you don't need to do minimal first.


I've reflashed using the firmware on the link, sonoff.ino.generic.bin, have restarted the sonoff, and am able to discover now through Sonoff Connect. Web interface for the device below, which now includes the "Configure Hubitat/Smarthings" option.

I've configured the hubitat parmeter host as the IP address of the Hubitat, and entered port 39501.

After restarting the device, I can again toggle on/off from device UI, but after several moments, I again get a similar error from the logs, and cannot toggle it on/off.

Device Log

dev:1962019-08-05 06:05:58.661 pm errorgroovy.lang.MissingMethodException: No signature of method: java.lang.String.encodeAsBase64() is applicable for argument types: () values: [] Possible solutions: decodeBase64() on line 339 (off)

dev:1962019-08-05 06:05:58.589 pm debug/cm?cmnd=Power%20Off

dev:1962019-08-05 06:05:58.586 pm debugoff()

dev:1962019-08-05 06:05:53.969 pm debugPOWER: ON

dev:1962019-08-05 06:05:53.965 pm debug========== Parsing Report ==========

dev:1962019-08-05 06:05:42.275 pm debugPOWER: OFF

dev:1962019-08-05 06:05:42.271 pm debug========== Parsing Report ==========

dev:1962019-08-05 06:05:39.737 pm debugPOWER: ON

dev:1962019-08-05 06:05:39.734 pm debug========== Parsing Report ==========

dev:1962019-08-05 06:05:30.742 pm errorgroovy.lang.MissingMethodException: No signature of method: java.lang.String.encodeAsBase64() is applicable for argument types: () values: [] Possible solutions: decodeBase64() on line 339 (on)

dev:1962019-08-05 06:05:30.363 pm debug/cm?cmnd=Power%20On

dev:1962019-08-05 06:05:30.362 pm debugon()

Sonoff Connect Log

app:2302019-08-05 06:08:46.969 pm debugDevice data: ( - reporting data: (

app:2302019-08-05 06:03:49.985 pm debugDevice data: ( - reporting data: (

app:2302019-08-05 06:03:45.170 pm debugDevice data: ( - reporting data: (

app:2302019-08-05 05:58:49.159 pm debugDevice data: ( - reporting data: (

app:2302019-08-05 05:53:47.139 pm debugDevice data: ( - reporting data: (

app:2302019-08-05 05:48:47.123 pm debugDevice data: ( - reporting data: (

app:2302019-08-05 05:46:00.482 pm debugSuccesfully added Sonoff device with ip address

app:2302019-08-05 05:45:57.242 pm debugCreating Sonoff Switch with dni: C44F3389A84C

app:2302019-08-05 05:45:57.236 pm debuguuid:38323636-4558-4dda-9188-cda0e689a84c={deviceAddress=50, ssdpTerm=urn:schemas-upnp-org:device:Basic:1, serialNumber=89a84c, hub=1, name=Sonoff Basic (, verified=true, ssdpPath=/description.xml, ssdpUSN=uuid:38323636-4558-4dda-9188-cda0e689a84c, networkAddress=C0A80157, mac=C44F3389A84C, ssdpNTS=null}

app:2302019-08-05 05:44:18.458 pm debugSonoff Basic


What driver are you using? The one from this page or the older driver?


@Ryan780 The Sonoff S20 from the link above Hubitat/sonoff-s20-tasmota.groovy at master · erocm123/Hubitat · GitHub . I'm going to try the driver on this page you posed 27 days ago with the additional changes noted in replies.


All works well, but a question, how do I use the child devices in the dashboard, you can set the main device as a switch but the child devices will turn the relay off but not on.


That shouldn't be the case. If it is, then the driver is not correct.


Any one here using the sonoff RF bridge?