Has someone integrated a Shelly TRV?

Hello,

The Spirit TRV (identically to the Aeon ones) are very unstable in my network and I'm interested into the new Shelly TRV (Wifi based). Has someone integrated this device in his network ? - and if so, how and what is the experience ?

Best regards,
Pascal

1 Like

@Evilborg has developed drivers for Shelly in the past, not sure about this device though.

Scott I’d be willing to order a couple and send one to you for testing, if that’d help.

1 Like

I have 1 but since I don't use radiator heat I'm at a loss on how to code the driver for it. I mean I have no idea what people would want it to do. I would need a practical use explanation.

In my case, I would like to be able to use Hubitat automations to control the valve such that it either opens fully, or closes fully, and that’s about it.

Admittedly that’s probably not how many other people would use a TRV, but I would use it to periodically shut off individual radiators when a room is unoccupied, or if it’s getting too hot in a room.

I want to use it as a replacement for my Spirit TRV, so I can explain how I use the spirit and what should also be nice to have working.

I don't control the valve directly, but that is possible with the Spirit, so it should be a nice to have for me.

These TRVs are controlled by their own logic on the board and need little control from the home automation.

What I do actually is :

  1. showing the temperature from the internal sensor on the dashboard. The TRV sends the temperature when this one changes by more than 1 °C, but this can be modified in the parameters of the TRV.

  2. Setting the mode of the TRV by my automations (Heat / OFF)

  3. Setting the temperature that the TRV should maintain (xx °C) by my automation

My rules use a virtual switch to turn heating on or completely off. This rule set's all TRVs to HEAT or to OFF.
Some rules are triggered by window contacts. Depending on the outside temperature the concerned TRVs are switched to OFF or are set to the deFrost temperature (5°C).
Some rules are triggered by time and set a night temperature (18 °C) a comfort temperature (21 °C) or a super comfort temperature (23° C).

Generally speaking, I think that it would be nice to have :

  • Temperature report
  • Valve opening report
  • Setting mode (HEAT, OFF, DEFROST if existing)
  • Setting the desired temperature
  • Setting the valve opening

I hope this is clear enough, and thank you for your interest in this thread.

1 Like

I have a very very basic driver for the TRV if anyone wants to test it out.

1 Like

So nobody wants to test the driver now? It will open and close and also set the target temp IF scheduling is enabled.

I ordered one :wink:.

1 Like

awesome... will have you do some testing and see what useful commands should be added except scheduling since doing it in the webui or the shelly app is better/faster.

I use a standard 2 wire actuator.

Controlled with konnected & nodemcu. Works ace.

Got the TRV installed. Can I get a copy of the driver?

Whoops.. Shelly Device Handlers for Hubitat

1 Like

Sorry but I’m not seeing the trv driver in that GitHub repo.

Edit: oh is it a gen2 device that uses the single Shelly-plus driver?

whoops didnt upload it yet... its very very beta right now which is why I probably didnt upload it yet and wanted feedback on it.

/**
 *
 *  Shelly TRV Driver
 *
 *  Copyright © 2018-2019 Scott Grayban
 *  Copyright © 2020 Allterco Robotics US
 *
 *  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.
 *
 * Hubitat is the Trademark and intellectual Property of Hubitat Inc.
 * Shelly is the Trademark and Intellectual Property of Allterco Robotics Ltd
 *
 *-------------------------------------------------------------------------------------------------------------------
 *
 *  1.0.0 - Initial code
 *
 */

import groovy.json.*
import groovy.transform.Field
def setVersion(){
	state.Version = "1.0.0"
	state.InternalName = "ShellyTRV"
}

metadata {
	definition (
		name: "Shelly TRV",
		namespace: "ShellyUSA",
		author: "Scott Grayban",
                importUrl: "https://raw.githubusercontent.com/ShellyUSA/Hubitat-Drivers/master/ShellyTRV.groovy"
		)
	{
        capability "Actuator"
        capability "Sensor"
        capability "Refresh"
        capability "Polling"
        capability "SignalStrength"
        capability "TemperatureMeasurement"
        capability "Valve"
        capability "Battery"
        
        attribute "FW_Update_Needed", "string"
        attribute "LastRefresh", "string"
        attribute "internal_tempC", "number"
        attribute "internal_tempF", "number"
        attribute "DeviceOverTemp", "string"
        attribute "MAC", "string"
        attribute "RelayChannel", "number"
        attribute "Primary_IP", "string"
        attribute "Primary_SSID", "string"
        attribute "Secondary_IP", "string"
        attribute "Secondary_SSID", "string"
        attribute "WiFiSignal", "string"
        attribute "Cloud", "string"
        attribute "Cloud_Connected", "string"
        attribute "energy", "number"
        attribute "DeviceType", "string"
        attribute "DeviceName", "string"
        attribute "NTPServer", "string"
        attribute "Position", "number"
        attribute "voltage", "number"
        
        command "RebootDevice"
        command "UpdateDeviceFW" // ota?update=1
        //command "updatecheck" // Only used for development
        command "getSettings"
        command "TargetTemp" , ["temp"]
	}

	preferences {
	def refreshRate = [:]
		refreshRate << ["1 min" : "Refresh every minute"]
		refreshRate << ["5 min" : "Refresh every 5 minutes"]
		refreshRate << ["15 min" : "Refresh every 15 minutes"]
		refreshRate << ["30 min" : "Refresh every 30 minutes"]
		refreshRate << ["manual" : "Manually or Polling Only"]

	input("ip", "string", title:"IP", description:"Shelly IP Address", defaultValue:"" , required: true)
	input name: "username", type: "text", title: "Username:", description: "(blank if none)", required: false
	input name: "password", type: "password", title: "Password:", description: "(blank if none)", required: false

	if (channel < 1) input name: "ntp_server", type: "text", title: "NTP time server:", description: "E.G. time.google.com or 192.168.0.59", defaultValue: "time.google.com", required: true

    // Only show for channel 0 since the device name is for the entire device
	if (channel < 1) input name: "devicename", type: "text", title: "Give your device a name:", description: "EG; Location/Room<br>NO SPACES in name", required: false
        
    input("refresh_Rate", "enum", title: "Device Refresh Rate", description:"<font color=red>!!WARNING!!</font><br>DO NOT USE if you have over 50 Shelly devices.", options: refreshRate, defaultValue: "manual")
        input "locale", "enum", title: "Choose refresh date format", required: true, defaultValue: true, options: [US:"US MM/DD/YYYY",UK:"UK DD/MM/YYYY"]

    input name: "debugOutput", type: "bool", title: "Enable debug logging?", defaultValue: true
	input name: "debugParse", type: "bool", title: "Enable JSON parse logging?", defaultValue: true
	input name: "txtEnable", type: "bool", title: "Enable descriptionText logging", defaultValue: true
	input name: "Shellyinfo", type: "text", title: "<center><font color=blue>Info Box</font><br>Shelly API docs are located</center>", 
        description: "<center><br><a href='http://shelly-api-docs.shelly.cloud/' title='shelly-api-docs.shelly.cloud' target='_blank'>[here]</a></center>"
	}
}

def initialize() {
	log.info "initialize"
	if (txtEnable) log.info "initialize"
}

def installed() {
    log.debug "Installed"
    state.DeviceName = "NotSet"
    state.RelayName = "NotSet"
}

def uninstalled() {
    unschedule()
    log.debug "Uninstalled"
}

def updated() {
    if (txtEnable) log.info "Preferences updated..."
    log.warn "Debug logging is: ${debugOutput == true}"
    log.warn "Switch protection is: ${settings?.protect}"
    unschedule()
    dbCleanUp()
    
    if (ip != null) { // Don't set until IP is saved
    if (!(getDataValue("model") in ["SHSW-1","SHEM"])) {
        sendSwitchCommand "/settings/relay/${channel}?max_power=${maxpower}"
    }
    if (getDataValue("model") in ["SHPLG-1","SHPLG-S","SHPLG-U1"]) {
        logDebug "LED setting ${led_status} and ${led_power}"
        sendSwitchCommand "/settings?led_status_disable=${led_status}"
        sendSwitchCommand "/settings?led_power_disable=${led_power}"
    }
    if (channel < 1) sendSwitchCommand "/settings?sntp_server=${ntp_server}"
// Set device and relay name
    if (channel < 1) sendSwitchCommand "/settings?name=${devicename}"
    if (!(getDataValue("model") in ["SHPLG-S","SHPLG-1","SHPLG-U1"])) sendSwitchCommand "/settings/relay/${channel}?name=${relayname}"

    if (getDataValue("model") == "SHEM") sendSwitchCommand "/settings/relay/${channel}?ctraf_type=${ctraf_type}"
    }
    
    switch(refresh_Rate) {
		case "1 min" :
			runEvery1Minute(autorefresh)
			break
		case "5 min" :
			runEvery5Minutes(autorefresh)
			break
		case "15 min" :
			runEvery15Minutes(autorefresh)
			break
		case "30 min" :
			runEvery30Minutes(autorefresh)
			break
		case "manual" :
			unschedule(autorefresh)
            log.info "Autorefresh disabled"
            break
	}
	if (txtEnable) log.info ("Auto Refresh set for every ${refresh_Rate} minute(s).")

    if (debugOutput) runIn(1800,logsOff) //Off in 30 minutes
    if (debugParse) runIn(300,logsOff) //Off in 5 minutes
    
    state.LastRefresh = new Date().format("YYYY/MM/dd \n HH:mm:ss", location.timeZone)

    version()
    refresh()
    getSettings()
}

private dbCleanUp() {
	state.remove("version")
	state.remove("Version")
	state.remove("ShellyfwUpdate")
	state.remove("power")
	state.remove("overpower")
	state.remove("dcpower")
	state.remove("max_power")
	state.remove("internal_tempC")
	state.remove("Status")
    state.remove("max_power")
    state.remove("RelayName")
    state.remove("RelayChannel")
    state.remove("powerSource")
}

def refresh(){
    if (ip != null) { // Don't set until IP is saved
    logDebug "Shelly Status called"
    getSettings()
    def params = [uri: "http://${username}:${password}@${ip}/status"]

try {
    httpGet(params) {
        resp -> resp.headers.each {
        logJSON "Response: ${it.name} : ${it.value}"
    }
        obs = resp.data
        logJSON "params: ${params}"
        logJSON "response contentType: ${resp.contentType}"
	    logJSON "response data: ${resp.data}"

        if (obs.temperature != null) sendEvent(name: "internal_tempC", value: obs.temperature)
        if (obs.tmp != null) {
            sendEvent(name: "internal_tempC", unit: "C", value: obs.tmp.tC)
            sendEvent(name: "internal_tempF", unit: "F", value: obs.tmp.tF)
        }
        if (obs.overtemperature != null) sendEvent(name: "DeviceOverTemp", value: obs.overtemperature)
        
        if (obs.wifi_sta != null) {
        state.rssi = obs.wifi_sta.rssi
        state.ssid = obs.wifi_sta.ssid
        state.ip = obs.wifi_sta.ip
        sendEvent(name: "Primary_SSID", value: state.ssid)
        sendEvent(name: "Primary_IP", value: state.ip)
        }
        
        sendEvent(name: "Charging", value: obs.charger)
        sendEvent(name: "calibrated", value: obs.calibrated)
        sendEvent(name: "voltage", value: obs.bat.voltage)
        sendEvent(name: "battery", unit: "%", value: obs.bat.value)
        
/*
-30 dBm	Excellent | -67 dBm	Good | -70 dBm	Poor | -80 dBm	Weak | -90 dBm	Dead
*/
        signal = state.rssi
        if (signal <= 0 && signal >= -70) {
            sendEvent(name:  "WiFiSignal", value: "<font color='green'>Excellent</font>", isStateChange: true);
        } else
        if (signal < -70 && signal >= -80) {
            sendEvent(name:  "WiFiSignal", value: "<font color='green'>Good</font>", isStateChange: true);
        } else
        if (signal < -80 && signal >= -90) {
            sendEvent(name: "WiFiSignal", value: "<font color='yellow'>Poor</font>", isStateChange: true);
        } else 
        if (signal < -90 && signal >= -100) {
            sendEvent(name: "WiFiSignal", value: "<font color='red'>Weak</font>", isStateChange: true);
        }

        state.mac = obs.mac
        sendEvent(name: "MAC", value: state.mac)
        sendEvent(name: "rssi", value: state.rssi)
        
// Device FW Updates
        state.has_update = obs.has_update
        if (state.has_update == true) {
            if (txtEnable) log.info "sendEvent NEW SHELLY FIRMWARE"
            sendEvent(name: "FW_Update_Needed", value: "<font color='red'>FIRMWARE Update Required</font>")
        }
        
        if (state.has_update == false) {
            if (txtEnable) log.info "sendEvent Device FW is current"
            sendEvent(name: "FW_Update_Needed", value: "<font color='green'>Device FW is current</font>")
        }

// Cloud
        state.cloud = obs.cloud.enabled
        if (state.cloud == true) {
            sendEvent(name: "Cloud", value: "<font color='green'>Enabled</font>")
        } else {
            sendEvent(name: "Cloud", value: "<font color='red'>Disabled</font>")
        }
        
        state.cloudConnected = obs.cloud.connected
        if (state.cloudConnected == true) {
            sendEvent(name: "Cloud_Connected", value: "<font color='green'>Connected</font>")
        } else {
            sendEvent(name: "Cloud_Connected", value: "<font color='red'>Not Connected</font>")
        }
        
// Relays
        if (obs.relays != null) {
        if (channel ==0) ison = obs.relays.ison[0]
        if (channel ==1) ison = obs.relays.ison[1]
        if (channel ==2) ison = obs.relays.ison[2]
        if (channel ==3) ison = obs.relays.ison[3]
        if (ison == true) {
            sendEvent(name: "switch", value: "on")
        } else {
            sendEvent(name: "switch", value: "off")
        }
    }
} // End try
       } catch (e) {
           log.error "something went wrong: $e"
       }
    } // End if !==ip      
} // End Refresh Status


// Get shelly device type
def getSettings(){
    if (ip != null) { // Don't set until IP is saved
    getThermostatsSettings()
    logDebug "Get Shelly Settings"
    def paramsSettings = [uri: "http://${username}:${password}@${ip}/settings"]

try {
    httpGet(paramsSettings) {
        respSettings -> respSettings.headers.each {
        logJSON "ResponseSettings: ${it.name} : ${it.value}"
    }
        obsSettings = respSettings.data

        logJSON "params: ${paramsSettings}"
        logJSON "response contentType: ${respSettings.contentType}"
	    logJSON "response data: ${respSettings.data}"

        state.DeviceType = obsSettings.device.type
        if (state.DeviceType == "SHSW-1") sendEvent(name: "DeviceType", value: "Shelly 1")
        if (state.DeviceType == "SHSW-PM") sendEvent(name: "DeviceType", value: "Shelly 1PM")
        if (state.DeviceType == "SHSW-21") sendEvent(name: "DeviceType", value: "Shelly 2")
        if (state.DeviceType == "SHSW-25") sendEvent(name: "DeviceType", value: "Shelly 2.5")
        if (state.DeviceType == "SHSW-44") sendEvent(name: "DeviceType", value: "Shelly 4Pro")
        if (state.DeviceType == "SHEM") sendEvent(name: "DeviceType", value: "Shelly EM")
        if (state.DeviceType == "SHEM-3") sendEvent(name: "DeviceType", value: "Shelly EM3")
        if (state.DeviceType == "SHPLG-1") sendEvent(name: "DeviceType", value: "Shelly Plug")
        if (state.DeviceType == "SHPLG-S") sendEvent(name: "DeviceType", value: "Shelly PlugS")
        if (state.DeviceType == "SHPLG-U1") sendEvent(name: "DeviceType", value: "Shelly Plug US")
        if (state.DeviceType == "SHTRV-01") sendEvent(name: "DeviceType", value: "Shelly TRV")


        state.ShellyHostname = obsSettings.device.hostname
        state.sntp_server = obsSettings.sntp.server
        sendEvent(name: "NTPServer", value: state.sntp_server)

        if (obsSettings.led_status_disable != null) {
            if (obsSettings.led_status_disable == false) {
                sendEvent(name: "LED_NetworkStatus", value: "<font color='green'>Enabled</font>")
            } else {
                sendEvent(name: "LED_NetworkStatus", value: "<font color='red'>Disabled</font>")
            }
            if (obsSettings.led_power_disable == false) {
                sendEvent(name: "LED_Output", value: "<font color='green'>Enabled</font>")
            } else {
                sendEvent(name: "LED_Output", value: "<font color='red'>Disabled</font>")
            }
        }

//Get Device name
       if (obsSettings.name != "NotSet") {
           state.DeviceName = obsSettings.name
           sendEvent(name: "DeviceName", value: state.DeviceName)
           updateDataValue("DeviceName", state.DeviceName)
           if (txtEnable) log.info "DeviceName is ${obsSettings.name}"
       } else if (obsSettings.name != null) {
           state.DeviceName = "NotSet"
           sendEvent(name: "DeviceName", value: state.DeviceName)
           if (txtEnable) log.info "DeviceName is ${obsSettings.name}"
       }
//Get Relay name
        if (getDataValue("model") != "SHPLG-S" && getDataValue("model") != "SHPLG-1" && getDataValue("model") != "SHPLG-U1" && getDataValue("model") != "SHEM") {
            if (obsSettings.relays != null) {
                if (channel == 0) relay_name = obsSettings.relays.name[0]
                if (channel == 1) relay_name = obsSettings.relays.name[1]
                if (channel == 2) relay_name = obsSettings.relays.name[2]
                if (channel == 3) relay_name = obsSettings.relays.name[3]
                if (relay_name != null) {
                    state.RelayName = relay_name
                    sendEvent(name: "RelayName", value: state.RelayName)
                    if (txtEnable) log.info "RelayName is ${relay_name}"
                } else {
                    state.RelayName = "NotSet"
                    sendEvent(name: "RelayName", value: state.RelayName)
                    if (txtEnable) log.info "RelayName is ${relay_name}"
                }
                updateDataValue("RelayName", state.RelayName)
            }
        } // The Plug devices do not offer a relay name

        if (getDataValue("model") == "SHEM") {
            state.RelayName = obsSettings.relays.name[0]
            sendEvent(name: "RelayName", value: state.RelayName)
            updateDataValue("RelayName", state.RelayName)
        }
        
        if (obsSettings.wifi_sta1 != null) {
        state.rssi = obsSettings.wifi_sta1.rssi
        state.Secondary_ssid = obsSettings.wifi_sta1.ssid
        state.Secondary_IP = obsSettings.wifi_sta1.ip
            if (obsSettings.wifi_sta1.enabled == true) sendEvent(name: "Secondary_SSID", value: state.Secondary_ssid)
            if (state.Secondary_IP != null) sendEvent(name: "Secondary_IP", value: state.Secondary_IP)
        }
       
        logDebug "updating data values"
        updateDataValue("model", state.DeviceType)
        updateDataValue("ShellyHostname", state.ShellyHostname)
        updateDataValue("ShellyIP", state.ip)
        updateDataValue("ShellySSID", obsSettings.wifi_sta.ssid)
        updateDataValue("manufacturer", "Allterco Robotics")
        updateDataValue("MAC", state.mac)
        updateDataValue("DeviceName", state.DeviceName)

} // End try
       } catch (e) {
           log.error "something went wrong: $e"
       }
    } // End if !==ip      
} // End getSettings Status


def getThermostatsSettings(){
    if (ip != null) { // Don't set until IP is saved
    logDebug "Shelly Thermostats Settings called"
    def params = [uri: "http://${username}:${password}@${ip}/thermostats/0"]

try {
    httpGet(params) {
        resp -> resp.headers.each {
        logJSON "Response: ${it.name} : ${it.value}"
    }
        obs = resp.data
        logJSON "params: ${params}"
        logJSON "response contentType: ${resp.contentType}"
	    logJSON "response data: ${resp.data}"

        sendEvent(name: "Position", value: obs.pos)
        if (obs.pos >= 1) {
            sendEvent(name: "valve", value: "open", isStateChange: true)
        } else
            sendEvent(name: "valve", value: "closed", isStateChange: true)
        sendEvent(name: "temperature", value: obs.tmp.value)

} // End try
       } catch (e) {
           log.error "something went wrong: $e"
       }
    } // End if !==ip      
} // End thermostat Status

def open() {
        logDebug "Executing Open"
        sendSwitchCommand "/thermostats/0?pos=100"
        sendEvent(name: "valve", value: "open")
}

def close() {
        logDebug "Executing Close"
        sendSwitchCommand "/thermostats/0?pos=0"
        sendEvent(name: "valve", value: "closed")
}

def TargetTemp(temp) {
        logDebug "Executing Target Temp"
        sendSwitchCommand "/settings/thermostats/0?target_t=${temp}"
}

def ping() {
	logDebug "ping"
	poll()
}

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

def autorefresh() {
    if (locale == "UK") {
	logDebug "Get last UK Date DD/MM/YYYY"
	state.LastRefresh = new Date().format("d/MM/YYYY \n HH:mm:ss", location.timeZone)
	sendEvent(name: "LastRefresh", value: state.LastRefresh, descriptionText: "Last refresh performed")
	} 
	if (locale == "US") {
	logDebug "Get last US Date MM/DD/YYYY"
	state.LastRefresh = new Date().format("MM/d/YYYY \n HH:mm:ss", location.timeZone)
	sendEvent(name: "LastRefresh", value: state.LastRefresh, descriptionText: "Last refresh performed")
	}
	if (txtEnable) log.info "Executing 'auto refresh'" //RK

    refresh()
}

private logJSON(msg) {
	if (settings?.debugParse || settings?.debugParse == null) {
	log.info "$msg"
	}
}

private logDebug(msg) {
	if (settings?.debugOutput || settings?.debugOutput == null) {
	log.debug "$msg"
	}
}

// handle commands
//RK Updated to include last refreshed
def poll() {
	if (locale == "UK") {
	logDebug "Get last UK Date DD/MM/YYYY"
	state.LastRefresh = new Date().format("d/MM/YYYY \n HH:mm:ss", location.timeZone)
	sendEvent(name: "LastRefresh", value: state.LastRefresh, descriptionText: "Last refresh performed")
	} 
	if (locale == "US") {
	logDebug "Get last US Date MM/DD/YYYY"
	state.LastRefresh = new Date().format("MM/d/YYYY \n HH:mm:ss", location.timeZone)
	sendEvent(name: "LastRefresh", value: state.LastRefresh, descriptionText: "Last refresh performed")
	}
	if (txtEnable) log.info "Executing 'poll'" //RK
	refresh()
}

def sendSwitchCommand(action) {
    if (txtEnable) log.info "Calling ${action}"
    def params = [uri: "http://${username}:${password}@${ip}/${action}"]
try {
    httpPost(params) {
        resp -> resp.headers.each {
        logDebug "Response: ${it.name} : ${it.value}"
    }
} // End try
        
} catch (e) {
        log.error "something went wrong: $e"
    }
    runIn(2, refresh)
}

def RebootDevice() {
    if (txtEnable) log.info "Rebooting Device"
    def params = [uri: "http://${username}:${password}@${ip}/reboot"]
try {
    httpPost(params) {
        resp -> resp.headers.each {
        logDebug "Response: ${it.name} : ${it.value}"
    }
} // End try
        
} catch (e) {
        log.error "something went wrong: $e"
    }
    runIn(15,refresh)
}

def UpdateDeviceFW() {
    if (txtEnable) log.info "Updating Device FW"
    def params = [uri: "http://${username}:${password}@${ip}/ota?update=1"]
try {
    httpPost(params) {
        resp -> resp.headers.each {
        logDebug "Response: ${it.name} : ${it.value}"
    }
} // End try
        
} catch (e) {
        log.error "something went wrong: $e"
    }
    runIn(30,refresh)
}

// Check Version   ***** with great thanks and acknowlegment to Cobra (github CobraVmax) for his original code **************
def version(){
	updatecheck()
	schedule("0 0 18 1/1 * ? *", updatecheck) // Cron schedule
//	schedule("0 0/1 * 1/1 * ? *", updatecheck) // Test Cron schedule
}

def updatecheck(){
	setVersion()
    def paramsUD = [uri: "https://raw.githubusercontent.com/ShellyUSA/Hubitat-Drivers/master/resources/version.json", contentType: "application/json; charset=utf-8"]
    try {
        httpGet(paramsUD) { respUD ->
            if (debugParse) log.debug " Version Checking - Response Data: ${respUD.data}"
            def copyrightRead = (respUD.data.copyright)
            state.Copyright = copyrightRead
            def newVerRaw = (respUD.data.versions.Driver.(state.InternalName))
            def newVer = (respUD.data.versions.Driver.(state.InternalName).replace(".", ""))
            def currentVer = state.Version.replace(".", "")
            state.UpdateInfo = (respUD.data.versions.UpdateInfo.Driver.(state.InternalName))
            state.author = (respUD.data.author)
            state.icon = (respUD.data.icon)
            if(newVer == "NLS"){
                state.DriverStatus = "<b>** This driver is no longer supported by $state.author  **</b>"
                log.warn "** This driver is no longer supported by $state.author **"
            } else
                if(newVer == "BETA"){
                state.Status = "<b>** THIS IS BETA CODE  **</b>"
                log.warn "** BETA CODE **"
            } else
                if(currentVer < newVer){
                state.DriverStatus = "<b>New Version Available (Version: $newVerRaw)</b>"
                log.warn "** There is a newer version of this driver available  (Version: $newVerRaw) **"
                log.warn "** $state.UpdateInfo **"
            } else
                if(currentVer > newVer){
                state.DriverStatus = "<b>You are using a Test version of this Driver (Version: $state.Version)</b>"
            } else {
                state.DriverStatus = "Current"
                log.info "You are using the current version of this driver"
            }
        } // httpGet
    } // try

    catch (e) {
        log.error "Something went wrong: CHECK THE JSON FILE AND IT'S URI -  $e"
    }
    if(state.DriverStatus == "Current"){
        state.UpdateInfo = "Up to date"
        sendEvent(name: "DriverUpdate", value: state.UpdateInfo)
        sendEvent(name: "DriverStatus", value: state.DriverStatus)
    } else {
        sendEvent(name: "DriverUpdate", value: state.UpdateInfo)
        sendEvent(name: "DriverStatus", value: state.DriverStatus)
    }

    sendEvent(name: "DriverAuthor", value: "sgrayban")
    sendEvent(name: "DriverVersion", value: state.Version)
}
3 Likes

Never used a Shelly device before (and my radiators are off since it’s summer), but it seems to be working fine.

The driver connected to the device no issue. I don’t have a password setup on it (yet). I can open/close the valve, and can set the temperature. There’s a “position” that runs 1-100 I think, it can be manually stepped in the device’s built-in webpage by 10% increments.


1 Like

I saw that just haven't added that function yet... figured it was summer for most of us here and wasn't needed yet :slight_smile:

Lol, not needed at all in my use case, so take your time.

But now at least I know these things should work and I can order a few more come winter.

Thanks for pushing a new driver out so quickly!

1 Like

Have you seen the new shelly Plus H&T ?

I haven’t had much reason to look at Shelly products before, the TRV was my first.

The temp and humidity sensor looks nice. I appreciate the display to be able to check quickly the current conditions in a room.

It’s also nice to see there’s at least one device manufacturer that makes it pretty simple to locally connect to their devices on a LAN, even though they also have a cloud-based functionality.

And cloud is NOT required.... that's a good thing!

And the driver for the new shelly Plus H&T is already done.

2 Likes