Help needed with custom DTH - Tasmota-HongTat

I am trying to monitor battery power via monitoring the VCC of ESP8266 (basically Vcc drops below 3V when the battery drops below 3.6V )
I am using Tasmota 9.5.0 firmware ( with USE_ADC_VCC enabled) and HongTat DHT.
I can Vcc value posted via a console on the actual Tasmota device (STATE = {“Vcc”:3.438}.
I have modified and published the DHT code in my IDE to show it up in the ST app.
Here is my issue - it would show up in a simulator but would not in the ST app.
I have tried to remove the device and re-add but still no luck.
I am no programmer by any means. :blush:
Any suggestions?
Original code is here
Here is modified code:

/**
 *  Tasmota - Generic Switch
 *
 *  Copyright 2020 AwfullySmart.com - HongTat Tan
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

String driverVersion() { return "20210810" }
import groovy.json.JsonSlurper
metadata {
    definition(name: "Tasmota Generic Switch", namespace: "hongtat", author: "HongTat Tan", ocfDeviceType: "oic.d.switch", vid: "af06f1a5-45b6-39ee-86bb-7cbe15062491", mnmn: "SmartThingsCommunity") {
        capability "Actuator"
        capability "Health Check"
        capability "Switch"
        capability "Polling"
        capability "Refresh"
        capability "Sensor"
        capability "Signal Strength"
		
        attribute "lastSeen", "string"
        attribute "version", "string"
    }

    simulator {
    }

    preferences {
        section {
            input(title: "Device Settings",
                    description: "To view/update this settings, go to the Tasmota (Connect) SmartApp and select this device.",
                    displayDuringSetup: false,
                    type: "paragraph",
                    element: "paragraph")
            input(title: "", description: "Tasmota Generic Switch v${driverVersion()}", displayDuringSetup: false, type: "paragraph", element: "paragraph")
        }
    }

    tiles(scale: 2) {
        multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
            tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
                attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#00A0DC"
                attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff"
            }
        }

        standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh"
        }
        standardTile("lqi", "device.lqi", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "default", label: 'LQI: ${currentValue}'
        }
        standardTile("rssi", "device.rssi", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "default", label: 'RSSI: ${currentValue}dBm'
        }
		standardTile("Vcc", "device.Vcc", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "default", label: 'Vcc: ${currentValue}V'
         }   
        main "switch"
        details(["switch", "refresh", "lqi", "rssi", "Vcc"])
    }
}

def installed() {
    sendEvent(name: "checkInterval", value: 30 * 60 + 2 * 60, displayed: false, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID])
    sendEvent(name: "switch", value: "off")
    log.debug "Installed"
    response(refresh())
}

def uninstalled() {
    sendEvent(name: "epEvent", value: "delete all", isStateChange: true, displayed: false, descriptionText: "Delete endpoint devices")
}

def updated() {
    initialize()
}

def initialize() {
    if (device.hub == null) {
        log.error "Hub is null, must set the hub in the device settings so we can get local hub IP and port"
        return
    }
    // Child creation
    String childMeta = getDataValue("child")
    if (childMeta != null && ((childMeta.startsWith("{") && childMeta.endsWith("}")) || (childMeta.startsWith("[") && childMeta.endsWith("]")))) {
        def json = new JsonSlurper().parseText(childMeta)
        if (json != null) {
            boolean hasError = false
            json.each { i,tasmota ->
                try {
                    String dni = "${device.deviceNetworkId}-ep${i}"
                    addChildDevice(tasmota, dni, device.getHub().getId(),
                            [completedSetup: true, label: "${device.displayName} ${i}", isComponent: false])
                    log.debug "Created '${device.displayName}' - ${i}ch (${tasmota})"
                } catch (all) {
                    hasError = true
                    log.error "Error: ${(all as String).split(":")[1]}."
                }
            }
            if (hasError == false) {
                updateDataValue("child","")
            }
        }
    }

    def syncFrequency = (parent.generalSetting("frequency") ?: 'Every 1 minute').replace('Every ', 'Every').replace(' minute', 'Minute').replace(' hour', 'Hour')
    try {
        "run$syncFrequency"(refresh)
    } catch (all) { }
    sendEvent(name: "checkInterval", value: parent.checkInterval(), displayed: false, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID])

    parent.callTasmota(this, "Status 5")
    parent.callTasmota(this, "Backlog Rule1 ON Power#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER\":\"%value%\"}} ENDON ON Power1#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER1\":\"%value%\"}} ENDON ON Power2#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER2\":\"%value%\"}} ENDON ON Power3#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER3\":\"%value%\"}} ENDON ON Power4#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER4\":\"%value%\"}} ENDON;Rule1 1")
    parent.callTasmota(this, "Backlog Rule2 ON Power5#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER5\":\"%value%\"}} ENDON ON Power6#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER6\":\"%value%\"}} ENDON ON Power7#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER7\":\"%value%\"}} ENDON ON Power8#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER8\":\"%value%\"}} ENDON;Rule2 1")
    parent.callTasmota(this, "Status 8")
    refresh()
}

def parse(String description) {
    def events = null
    def message = parseLanMessage(description)
    def json = parent.getJson(message.header)
    if (json != null) {
        events = parseEvents(200, json)
    }
    return events
}

def calledBackHandler(physicalgraph.device.HubResponse hubResponse) {
    def events = null
    def status = hubResponse.status
    def json = hubResponse.json
    events = parseEvents(status, json)
    return events
}

def parseEvents(status, json) {
    def events = []
    if (status as Integer == 200) {
        def channel = getDataValue("endpoints")?.toInteger()
        def eventdateformat = parent.generalSetting("dateformat")
        def now = location.timeZone ? new Date().format("${eventdateformat}a", location.timeZone) : new Date().format("yyyy MMM dd EEE h:mm:ss")

        // Power
        if (channel != null) {
            for (i in 0..channel) {
                def number = (i > 0) ? i : ""
                def power = (json?.StatusSTS?."POWER${number}" != null) ? (json?.StatusSTS?."POWER${number}") : ((json?."POWER${number}" != null) ? json?."POWER${number}" : null)

                def powerStatus = null
                if (power in ["ON", "1"]) {
                    powerStatus = "on"
                } else if (power in ["OFF", "0"]) {
                    powerStatus = "off"
                }
                if (powerStatus != null) {
                    if ((channel == 1) || (channel > 1 && i == 1)) {
                        events << sendEvent(name: "switch", value: powerStatus)
                    } else {
                        String childDni = "${device.deviceNetworkId}-ep$i"
                        def child = childDevices.find { it.deviceNetworkId == childDni }
                        child?.sendEvent(name: "switch", value: powerStatus)
                    }
                    log.debug "Switch $number: '$powerStatus'"
                }
            }
        }

        // Temperature, Humidity (Status 8)
        def resultTH = null
        if (json?.StatusSNS != null) {
            for (record in json.StatusSNS) {
                if (record.value instanceof Map && (record.value.containsKey("Humidity") || record.value.containsKey("Temperature"))) {
                    resultTH = record.value
                }
            }
            if (resultTH != null) {
                def childMessage = [:]
                if (resultTH.containsKey("Humidity")) {
                    childMessage.humidity = Math.round((resultTH.Humidity as Double) * 100) / 100
                }
                if (resultTH.containsKey("Temperature")) {
                    childMessage.temperature = resultTH.Temperature.toFloat()
                }
                if (json?.StatusSNS?.TempUnit != null) {
                    childMessage.tempUnit = json?.StatusSNS?.TempUnit
                }
                def child = childDevices.find { it.typeName == "Tasmota Child Temp/Humidity Sensor" }
                child?.parseEvents(200, childMessage)
            }
        }

        // MAC
        if (json?.StatusNET?.Mac != null) {
            def dni = parent.setNetworkAddress(json.StatusNET.Mac)
            def actualDeviceNetworkId = device.deviceNetworkId
            if (actualDeviceNetworkId != state.dni) {
                runIn(10, refresh)
            }
            log.debug "MAC: '${json.StatusNET.Mac}', DNI: '${state.dni}'"
            if (state.dni == null || state.dni == "" || dni != state.dni) {
                if (channel > 1 && childDevices) {
                    childDevices.each {
                        it.deviceNetworkId = "${dni}-ep" + parent.channelNumber(it.deviceNetworkId)
                        log.debug "Child: " + "${dni}-ep" + parent.channelNumber(it.deviceNetworkId)
                    }
                }
            }
            state.dni = dni
        }

        // Signal Strength
        if (json?.StatusSTS?.Wifi != null) {
            events << sendEvent(name: "lqi", value: json?.StatusSTS?.Wifi.RSSI, displayed: false)
            events << sendEvent(name: "rssi", value: json?.StatusSTS?.Wifi.Signal, displayed: false)
            
         }
          // Vcc
        if (json?.StatusSTS?.Vcc != null) {
            events << sendEvent(name: "Vcc", value: json?.StatusSTS?.Vcc, displayed: false)
        }


        // Version
        if (json?.StatusFWR?.Version != null) {
            state.lastCheckedVersion = new Date().getTime()
            events << sendEvent(name: "version", value: json.StatusFWR.Version, displayed: false)
        }

        // Call back
        if (json?.cb != null) {
            parent.callTasmota(this, json.cb)
        }

        // Last seen
        events << sendEvent(name: "lastSeen", value: now, displayed: false)
    }
    return events
}

def on() {
    def channel = getDataValue("endpoints")?.toInteger()
    parent.callTasmota(this, "POWER" + ((channel == 1) ? "" : 1) + " 1")
}

def off() {
    def channel = getDataValue("endpoints")?.toInteger()
    parent.callTasmota(this, "POWER" + ((channel == 1) ? "" : 1) + " 0")
}

def refresh(dni=null) {
    def lastRefreshed = state.lastRefreshed
    if (lastRefreshed && (now() - lastRefreshed < 5000)) return
    state.lastRefreshed = now()

    // Check version every 30m
    def lastCheckedVersion = state.lastCheckedVersion
    if (!lastCheckedVersion || (lastCheckedVersion && (now() - lastCheckedVersion > (30 * 60 * 1000)))) {
        parent.callTasmota(this, "Status 2")
    }

    def actualDeviceNetworkId = device.deviceNetworkId
    if (state.dni == null || state.dni == "" || actualDeviceNetworkId != state.dni) {
        parent.callTasmota(this, "Status 5")
    }
    parent.callTasmota(this, "Status 8")
    parent.callTasmota(this, "Status 11")
}

def ping() {
    refresh()
}

def childOn(dni) {
    parent.callTasmota(this, "POWER" + parent.channelNumber(dni) + " 1")
}

def childOff(dni) {
    parent.callTasmota(this, "POWER" + parent.channelNumber(dni) + " 0")
}

I wonder if you are getting bit by the caching issue I read about.

But you are just rearranging deck chairs yes?

the caching issue - possibly (tonight will 24 hours), but I did not change vid, just added one more tile to show Vcc value.

Hi! Currently, the DTH’s tiles are no longer used to setup the device UI, so even if VCC is included there, it won’t appear in the app, that’s what the VID is used for.
Also, you need to use a supported capability for this, eg. voltageMeasurement and instead of sending an event to “Vcc”, you should send it to “voltage”:

events << sendEvent(name: "voltage", value: json?.StatusSTS?.Vcc, displayed: false)

Remember that you have to:

  • Add this capability in the DTH’s definition
capability "voltageMeasurement"
  • Create a new device configuration. Have you seen this process? If not I can guide you through it.
  • Avoid the caching issue by creating a new DTH with the same config but using the new VID (and re-installing the device)

Nayely.
Thanks for your reply.
As I said - I have small to none programming knowledge. :thinking:
I have change DTH in my IDE per your suggestion and not Voltage showed up in IDE GUI

But not in the App - is it a caching issue or do I still have to “Create a new device configuration”

Yes, even if the values appear in the IDE because it is part of the definition, but it is not included in the device presentation UI, so the app doesn’t display the value.

  1. Copy the JSON in the sample below
  2. In case you don’t have it yet, create a Personal Access Token (PAT)
  3. Make a request to the API using Postman
    1. In the Authorization tab, choose the type of “Bearer Token” and paste there the PAT you created above.
    2. In “Body”, select “raw” > “JSON” and put the JSON of the sample
    3. Select the request type “POST” and enter the URL https://api.smartthings.com/v1/presentation/deviceconfig
    4. Click on “Send”. See the picture below.

1 Like

Thank you!
I have completed steps above!
Got:
“presentationId”: “17deXXXXX-393c-b1c3-XXXXXXXXX1”,
“manufacturerName”: “SmartThingsCommunity”

What is the next?

  1. Copy the current configuration of the DTH “Tasmota Generic Switch”
  2. Select “Create new Device Handler” and paste the configuration in “from Code”, before saving, replace the value in vid: "af06f1a5-45b6-39ee-86bb-7cbe15062491" with the new value and change its name.
  3. Click on “Create”. I believe there’s a SmartApp that uses this device handler, right?
    You need to change all the references to this DTH using the new name, eg. see the line of the SmartApp selected below
//Instead of:
"1000": [name: "Generic Switch (1ch)", type: "Tasmota Generic Switch"],

//It would be the new name that you used, this is an example:
"1000": [name: "Generic Switch (1ch)", type: "Tasmota Generic Switch 2"],
  1. Look for all the references to Tasmota Generic Switch using ctrl+F and do the same.
  2. Once you re-install your device, make sure it is using the new DTH by getting the devices list, also with Postman. Look for the device and check the property “deviceTypeName” where the DTH name the device uses is located.

Thank you, Nayely!
IT IS WORKING!

1 Like

Anytime, @AlecBL! :grinning_face_with_smiling_eyes: Remember to mark the solution of your post so others can take reference from it.

Thanks!

One more question - Is it possible to see the last provided reading?
I would like to enable a deep-sleep mode to save battery, and it would be very helpful to see historic data, but it is empty in the ST app.

Do you mean in the device details > history?
This is odd, it should have the registry of the recent capability events.
Using the URL shared in the post below, you can get the Device History from the API:

Also, just so you’re aware, sometimes the sleepy devices become offline in the app, I believe it wasn’t enrolled for the health check (which sends a ping periodically to see if the device is available)

Nayely,
Sorry to bug you, but how do I add temperature measurement capability to get a sensor reading in the same window, so I could see the Signal level, Battery and Temperature.
Here is a DHT code that I am want to use -

String driverVersion() { return "20200913" }
metadata {
    definition(name: "Tasmota Child Temp/Humidity Sensor", namespace: "hongtat", author: "HongTat Tan", ocfDeviceType: "oic.d.thermostat", vid: "60f649d4-4d75-32f0-9002-6c50d1380815", mnmn: "SmartThingsCommunity") {
        capability "Configuration"
        capability "Refresh"
        capability "Temperature Measurement"
        capability "Relative Humidity Measurement"
        capability "Health Check"
        capability "Sensor"
    }

    preferences {
        input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
        input "humidityOffset", "number", title: "Humidity Offset", description: "Adjust humidity by this percentage", range: "*..*", displayDuringSetup: false
        input(title: "", description: "Tasmota Child Temp/Humidity Sensor v${driverVersion()}", displayDuringSetup: false, type: "paragraph", element: "paragraph")
    }

    tiles(scale: 2) {
        multiAttributeTile(name: "temperature", type: "generic", width: 6, height: 4, canChangeIcon: true) {
            tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
                attributeState "temperature", label: '${currentValue}°',
                    backgroundColors:[
                        // Celsius
                        [value: 0, color: "#153591"],
                        [value: 7, color: "#1e9cbb"],
                        [value: 15, color: "#90d2a7"],
                        [value: 23, color: "#44b621"],
                        [value: 28, color: "#f1d801"],
                        [value: 35, color: "#d04e00"],
                        [value: 37, color: "#bc2323"],
                        // Fahrenheit
                        [value: 40, color: "#153591"],
                        [value: 44, color: "#1e9cbb"],
                        [value: 59, color: "#90d2a7"],
                        [value: 74, color: "#44b621"],
                        [value: 84, color: "#f1d801"],
                        [value: 95, color: "#d04e00"],
                        [value: 96, color: "#bc2323"]
                    ]
            }
        }
        valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
            state "humidity", label: '${currentValue}% humidity', unit: ""
        }
        standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
            state "default", action: "refresh.refresh", icon: "st.secondary.refresh"
        }

        main "temperature", "humidity"
        details(["temperature", "humidity", "refresh"])
    }
}

def installed() {
    sendEvent(name: "checkInterval", value: 30 * 60 + 2 * 60, displayed: false, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID])
    sendEvent(name: "temperature", value: 0, unit: "C", displayed: false)
    sendEvent(name: "humidity", value: 0, unit: "%", displayed: false)
}

def parse(String description) {
}

def parseEvents(status, json) {
    if (status as Integer == 200) {
        log.debug "Event: ${json}"
        if (json?.temperature != null) {
            def map = [:]
            def temp = convertTemperatureIfNeeded(json?.temperature?.toFloat(), ((json?.tempUnit == "C") ? "C" : "F"), 1).toFloat()
            map.name = "temperature"
            if (tempOffset) {
                map.value = Math.round((temp + (int) tempOffset) * 100) / 100
            } else {
                map.value = Math.round(temp * 100) / 100
            }
            map.unit = getTemperatureScale()
            sendEvent(map)
        }
        if (json?.humidity != null) {
            sendEvent(name: "humidity", value: ((humidityOffset) ? ((int) json?.humidity + (int) humidityOffset) : json?.humidity), unit: "%")
        }
    }
}

def ping() {
    // Intentionally left blank as parent should handle this
}

def refresh() {
    parent.refresh(device.deviceNetworkId)
}

def configure() {
}

def uninstalled() {
    parent.delete()
>

}

The process to add a new capabilty is the same as we did before:

  1. Add the capability in the DTH definition
  2. Make sure the events to the capability are being sent correctly (using the attribute name)
  • VoltageMeasurement > attribute: voltage
  • TemperatureMeasurement > attribute: temperature
  1. Create a new device presentation
  2. Avoid the cache issue by creating a new DTH using the new VID
  3. Change the references of the DTH in the parent handler.

The DTH you mention seems to be controlled by a parent DTH because the name is “child” and there’s no content in parse()). In that case, you need to do the same steps, but:

  • 2 and 4 will be done in the Parent directly. You need to find where it is being used (use ctrl+F in all the DTHs and SmartApp related)

Also, I checked the presentation (60f649d4-4d75-32f0-9002-6c50d1380815) and it currently has these capabilities:

  • temperatureMeasurement
  • relativeHumidityMeasurement

Well, I do not know how to properly form the code.
Here is what I was able to come up with:

/**
 *  Tasmota - Specific Switch
 *
 *  Copyright 2020 AwfullySmart.com - HongTat Tan
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

String driverVersion() { return "20210813" }
import groovy.json.JsonSlurper
metadata {
    definition(name: "Tasmota Generic Switch", namespace: "hongtat", author: "HongTat Tan", ocfDeviceType: "oic.d.switch", vid: "b1677d39-c6bf-31d1-a0d6-e0dbefaa0070", mnmn: "SmartThingsCommunity") {
        capability "Actuator"
        capability "Health Check"
        capability "Switch"
        capability "Polling"
        capability "Refresh"
        capability "Sensor"
        capability "Signal Strength"
		capability "voltageMeasurement"
		capability "Temperature Measurement"

        attribute "lastSeen", "string"
        attribute "version", "string"
    }

    simulator {
    }

    preferences {
        section {
            input(title: "Device Settings",
                    description: "To view/update this settings, go to the Tasmota (Connect) SmartApp and select this device.",
                    displayDuringSetup: false,
                    type: "paragraph",
                    element: "paragraph")
            input(title: "", description: "Tasmota Specific Switch v${driverVersion()}", displayDuringSetup: false, type: "paragraph", element: "paragraph")
        }
		}
    tiles(scale: 2) {
        multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
            tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
                attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#00A0DC"
                attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff"
            }
        }

        standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh"
        }
        standardTile("lqi", "device.lqi", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "default", label: 'LQI: ${currentValue}'
        }
        standardTile("rssi", "device.rssi", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "default", label: 'RSSI: ${currentValue}dBm'
        }
		standardTile("Vcc", "device.Vcc", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "default", label: 'Vcc: ${currentValue}V'
         }   
		standardTile("Temperature", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state "default", label: 'Temperature: ${currentValue}'
         }
        main "switch"
        details(["switch", "refresh", "lqi", "rssi", "Vcc", "Temperature"])
    }
	}	


def installed() {
    sendEvent(name: "checkInterval", value: 30 * 60 + 2 * 60, displayed: false, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID])
    sendEvent(name: "switch", value: "off")
	sendEvent(name: "temperature", value: 0, unit: "C", displayed: false)
    log.debug "Installed"
    response(refresh())
}

def uninstalled() {
    sendEvent(name: "epEvent", value: "delete all", isStateChange: true, displayed: false, descriptionText: "Delete endpoint devices")
}

def updated() {
    initialize()
}

def initialize() {
    if (device.hub == null) {
        log.error "Hub is null, must set the hub in the device settings so we can get local hub IP and port"
        return
    }
    

    def syncFrequency = (parent.generalSetting("frequency") ?: 'Every 1 minute').replace('Every ', 'Every').replace(' minute', 'Minute').replace(' hour', 'Hour')
    try {
        "run$syncFrequency"(refresh)
    } catch (all) { }
    sendEvent(name: "checkInterval", value: parent.checkInterval(), displayed: false, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID])

    parent.callTasmota(this, "Status 5")
    parent.callTasmota(this, "Backlog Rule1 ON Power#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER\":\"%value%\"}} ENDON ON Power1#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER1\":\"%value%\"}} ENDON ON Power2#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER2\":\"%value%\"}} ENDON ON Power3#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER3\":\"%value%\"}} ENDON ON Power4#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER4\":\"%value%\"}} ENDON;Rule1 1")
    parent.callTasmota(this, "Backlog Rule2 ON Power5#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER5\":\"%value%\"}} ENDON ON Power6#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER6\":\"%value%\"}} ENDON ON Power7#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER7\":\"%value%\"}} ENDON ON Power8#state DO WebSend ["+device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")+"] /?json={\"StatusSTS\":{\"POWER8\":\"%value%\"}} ENDON;Rule2 1")
    parent.callTasmota(this, "Status 8")
    refresh()
}

def parse(String description) {
    def events = null
    def message = parseLanMessage(description)
    def json = parent.getJson(message.header)
    if (json != null) {
        events = parseEvents(200, json)
    }
    return events
}

def calledBackHandler(physicalgraph.device.HubResponse hubResponse) {
    def events = null
    def status = hubResponse.status
    def json = hubResponse.json
    events = parseEvents(status, json)
    return events
}

def parseEvents(status, json) {
    def events = []
    if (status as Integer == 200) {
        def channel = getDataValue("endpoints")?.toInteger()
        def eventdateformat = parent.generalSetting("dateformat")
        def now = location.timeZone ? new Date().format("${eventdateformat}a", location.timeZone) : new Date().format("yyyy MMM dd EEE h:mm:ss")

        // Power
        if (channel != null) {
            for (i in 0..channel) {
                def number = (i > 0) ? i : ""
                def power = (json?.StatusSTS?."POWER${number}" != null) ? (json?.StatusSTS?."POWER${number}") : ((json?."POWER${number}" != null) ? json?."POWER${number}" : null)

                def powerStatus = null
                if (power in ["ON", "1"]) {
                    powerStatus = "on"
                } else if (power in ["OFF", "0"]) {
                    powerStatus = "off"
                }
                if (powerStatus != null) {
                    if ((channel == 1) || (channel > 1 && i == 1)) {
                        events << sendEvent(name: "switch", value: powerStatus)
                    } else {
                        String childDni = "${device.deviceNetworkId}-ep$i"
                        def child = childDevices.find { it.deviceNetworkId == childDni }
                        child?.sendEvent(name: "switch", value: powerStatus)
                    }
                    log.debug "Switch $number: '$powerStatus'"
                }
            }
        }
		// Temperature
        log.debug "Event: ${json}"
        if (json?.temperature != null) {
            def map = [:]
            def temp = convertTemperatureIfNeeded(json?.temperature?.toFloat(), ((json?.tempUnit == "C") ? "C" : "F"), 1).toFloat()
            map.name = "temperature"
            if (tempOffset) {
                map.value = Math.round((temp + (int) tempOffset) * 100) / 100
            } else {
                map.value = Math.round(temp * 100) / 100
            }
            map.unit = getTemperatureScale()
            sendEvent(map)
        }
                
        
        // MAC
        if (json?.StatusNET?.Mac != null) {
            def dni = parent.setNetworkAddress(json.StatusNET.Mac)
            def actualDeviceNetworkId = device.deviceNetworkId
            if (actualDeviceNetworkId != state.dni) {
                runIn(10, refresh)
            }
            log.debug "MAC: '${json.StatusNET.Mac}', DNI: '${state.dni}'"
            if (state.dni == null || state.dni == "" || dni != state.dni) {
                if (channel > 1 && childDevices) {
                    childDevices.each {
                        it.deviceNetworkId = "${dni}-ep" + parent.channelNumber(it.deviceNetworkId)
                        log.debug "Child: " + "${dni}-ep" + parent.channelNumber(it.deviceNetworkId)
                    }
                }
            }
            state.dni = dni
        }

        // Signal Strength
        if (json?.StatusSTS?.Wifi != null) {
            events << sendEvent(name: "lqi", value: json?.StatusSTS?.Wifi.RSSI, displayed: false)
            events << sendEvent(name: "rssi", value: json?.StatusSTS?.Wifi.Signal, displayed: false)
            
         }
          // VoltageMeasurement
        if (json?.StatusSTS?.Vcc != null) {
			events << sendEvent(name: "voltage", value: json?.StatusSTS?.Vcc, displayed: false)
		}


        // Version
        if (json?.StatusFWR?.Version != null) {
            state.lastCheckedVersion = new Date().getTime()
            events << sendEvent(name: "version", value: json.StatusFWR.Version, displayed: false)
        }

        // Call back
        if (json?.cb != null) {
            parent.callTasmota(this, json.cb)
        }

        // Last seen
        events << sendEvent(name: "lastSeen", value: now, displayed: false)
    }
    return events
}

def on() {
    def channel = getDataValue("endpoints")?.toInteger()
    parent.callTasmota(this, "POWER" + ((channel == 1) ? "" : 1) + " 1")
}

def off() {
    def channel = getDataValue("endpoints")?.toInteger()
    parent.callTasmota(this, "POWER" + ((channel == 1) ? "" : 1) + " 0")
}

def refresh(dni=null) {
    def lastRefreshed = state.lastRefreshed
    if (lastRefreshed && (now() - lastRefreshed < 5000)) return
    state.lastRefreshed = now()

    // Check version every 30m
    def lastCheckedVersion = state.lastCheckedVersion
    if (!lastCheckedVersion || (lastCheckedVersion && (now() - lastCheckedVersion > (30 * 60 * 1000)))) {
        parent.callTasmota(this, "Status 2")
    }

    def actualDeviceNetworkId = device.deviceNetworkId
    if (state.dni == null || state.dni == "" || actualDeviceNetworkId != state.dni) {
        parent.callTasmota(this, "Status 5")
    }
    parent.callTasmota(this, "Status 8")
    parent.callTasmota(this, "Status 11")
}

def ping() {
    refresh()
}

def childOn(dni) {
    parent.callTasmota(this, "POWER" + parent.channelNumber(dni) + " 1")
}

def childOff(dni) {
    parent.callTasmota(this, "POWER" + parent.channelNumber(dni) + " 0")
}

Let me see if I understand what you mean:

Do you want to modify the Tasmota Generic Switch handler again and add Temperature Measurement as you saw it in Tasmota Child Temp/Humidity Sensor?

If you’re receiving the temperature event from the device, you need to add the corresponding section in parseEvent() (as you did).

What was your result? I tried to get the presentation b1677d39-c6bf-31d1-a0d6-e0dbefaa0070 and it says it doesn’t exist.

Do you want to modify the Tasmota Generic Switch handler again and add Temperature Measurement as you saw it in Tasmota Child Temp/Humidity Sensor ?

Correct!

If you’re receiving the temperature event from the device, you need to add the corresponding section in parseEvent() (as you did).

It does not work - it will not show up in the ST app, not even tile/section (I guess the “tile” is old term)

Looks like it is working now, It was a syntax mistake in the App (stupid me :disappointed:)
Thanks again!

Sorry, spoke to soon. It is showing up the Temperature tile in the ST app, but with 0.0. Looks like it will not read from the device. Could you please let me know how to check this through postman ?

Do you see the log from the event?
It would be helpful if you added more log prints to see the flow of the event:

If this value is still 0 in the ST app and in the Device status, then the event is not being saved correctly from the handler.