Has someone integrated a Shelly TRV?

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

Hi.
Just bought a couple of the Shelly TRVs - Slightly tortuous connection (compared to Z-Wave.....), but seems to be working nicely, even updated the firmware :slight_smile: Thanks!
Will the TRV driver code make its way into the repository at some point?
Cheers.

And..... Whilst in the 'device edit' state, I can see all the nice thermostat related things, elsewhere, they just look like either valves or temperature sensors......

Quick prod in this topic - is there any ongoing support for the Shelly TRVs working properly as thermostats...?
Thanks!

OK, my next Spirit is gone out of life. Can I finally conclude that the Shelly TRV didn't make his way to Hubitat and that it's not a choice ?

My Shelly TRV has been working fine with Scott’s driver code posted above.

No, you can't.
My Shelly TRV works like a charm.

OK. I thought so because the driver does not figure on the official driver page. Is this an official driver release ?
Are you able to set and get the following information :

Many thanks in advance for your feedback that will be very valuable for me :wink:

No, there is no "official" driver, but the already mentioned driver from @Evilborg:

TBH I don't know what :arrow_up: this :arrow_up: driver exactly supports, as I'm using my own Shelly drivers (I prefer minimalistic drivers and do the config via Shellys' own GUI).