Drayton Wiser Heating - port from ST?

aha! joined. woo!

Shall report back if I manage to progress. One step closer to sacking off smartthings !!!!

@BorrisTheCat

Righto, I've added it, but the temperature is reading some mental figure.

Haven't got much further due to car troubles ! grr..

That looks a bit like the one I bought to try out..............

Is your car a Zigbee or a Z-Wave one...... C7's have been giving trouble with Z-Wave devices.......

trv is zigbee - c7 hub.

I know that - I was asking about your car issues.......

gah, lack of sleep!

picasso c4, air suspension. what a load of old crap.

just swapped for springs instead of a tonne of electrical rap for self-levelling. christ.

There you go - it's a C4 - upgrade it to a C7 - I see the new C7 firmware just got released !!!!

So the converted driver isn't working for me unfortunately.

  1. Can set the setpoint fine, but
  2. The thermostatoperatingstate doesn't seem to change (so trv doesn't open/close)
  3. The temp offset is something like 80 degs (celcius) out.

Can anyone possibly assist please? This is the main use of 'smart' in our home, and I'd love to decommission my Samsung hub.

This is a link to the driver I'm using on smartthings:
GitHub - GSzabados/SmartThings-Wiser-iTRV: Wiser iTRV Device Handler

I contacted the author of the above, who has no plans to port to hubitat. He did however point me towards this:

But due to myself not being a coder, this doesn't help!

Can anyone please assist? I'd love to get this working =)

here is a port of the app if no one else has done it.. i dont have one so cannot test but it does compile/save

Thanks. I've actually replaced my Drayton trvs with cheap dumb 240v electric ones for about 6 quid a shot. I'm completing the install tomorrow, and expecting an overall dramatic improvement.

Is there anyone who could spare the time to look at the device handlers all 4 of them and possibly port them from st to Hubitat. I have tried the app that Kahn has ported but it fails on creating the device handlers.
Thanks in advance.

is there anyone who could explain these logs to me please
app:1732020-11-15 01:50:47.235 pm debugError creating child device com.hubitat.app.exception.UnknownDeviceTypeException: Device type 'Drayton Wiser TRV' in namespace 'null' not found

app:1732020-11-15 01:50:47.230 pm debugError creating child device com.hubitat.app.exception.UnknownDeviceTypeException: Device type 'Drayton Wiser TRV' in namespace 'null' not found

app:1732020-11-15 01:50:47.224 pm debugError creating child device com.hubitat.app.exception.UnknownDeviceTypeException: Device type 'Drayton Wiser TRV' in namespace 'null' not found

app:1732020-11-15 01:50:47.219 pm debugError creating child device com.hubitat.app.exception.UnknownDeviceTypeException: Device type 'Drayton Wiser TRV' in namespace 'null' not found

app:1732020-11-15 01:50:47.214 pm debugError creating child device com.hubitat.app.exception.UnknownDeviceTypeException: Device type 'Drayton Wiser TRV' in namespace 'null' not found

app:1732020-11-15 01:50:47.206 pm debugError creating child device com.hubitat.app.exception.UnknownDeviceTypeException: Device type 'Drayton Wiser Hub' in namespace 'null' not found

app:1732020-11-15 01:50:47.194 pm debugcreateChildDevices()

app:1732020-11-15 01:50:47.192 pm debugNo hot water

app:1732020-11-15 01:50:47.191 pm debug200

app:1732020-11-15 01:50:47.190 pm debugEntered calledBackHandler()...

app:1732020-11-15 01:50:47.049 pm debuggetHubConfig()

@mdwaring, I've recently installed the Wiser system and thought I'd try integrating to Hubitat to automate the away mode when we leave home. I'm new to Hubitat but will try and give this a whirl.

So this error is being caused by the "app.namespace" in the app code, if you find/replace that with the actual namespace, "colc1705" then it will work (not sure if there's a proper way to reference the namespace from within the app but this works for now?!)

Looks like there are some other problems in this though, the window functions are missing in the room driver, so you could comment them out, but I'm going to have a go it getting them working if I can...

Hi Matt, I have actually got this working very well now. I spent a lot of time messing with this and now have it all working ( I think).
As soon as I have finished testing I will post the app and drivers up for anyone else who wants it.
Mark.

HI Mark, how did you get on testing the app and drivers for the Drayton Wiser Heating. Do you have code for the app and drivers you can share? I have the same Wiser system and it would be nice to have it talking to Hubitat. I tried with the Smartthings port but failed miserably.
If you can help it would be greatly appreciated.
Neil

Hi Neil,
Have had this running for a few months and seems to work well. The only thing I didnโ€™t do is get the hot water driver running as I donโ€™t use that side of the system.
Radiators and heat works great.
Iโ€™m at work at the moment but when I get time later I will post the drivers and app for you to use.
Mark.

HI Mark,
thanks for getting back to me. That would be great if you could post the drivers and app. I do not use the hot water function either so looks like yours would be the same configuration as mine.
Neil

/**

  • Drayton Wiser
  • Copyright 2018 Colin Chapman
  • 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.

*/
definition(
name: "Drayton Wiser (Connect)",
namespace: "colc1705",
author: "Colin Chapman",
description: "Connect Drayton Wiser to Smartthings",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
singleInstance: true)

preferences {

page(name: "mainPage", title:"Drayton Wiser Setup", content:"mainPage", install: true)

}

def installed() {
if (showDebug) log.debug "Installed with settings: ${settings}"

initialize()

}

def updated() {
if (showDebug) log.debug "Updated with settings: ${settings}"
unschedule()
unsubscribe()
initialize()
}

def initialize() {
if (showDebug) log.debug "initialize()"
getHubConfig()
runEvery1Minute("getHubConfig")
//getHubUrl("/data/domain/Room")
//sendMessageToHeatHub("/data/domain/", "GET", "")
}

def mainPage() {
if (showDebug) log.debug "mainPage"
return dynamicPage(name: "mainPage", title: "", install: true, uninstall: true) {
section("Title") {
input("hubIP", "string", title: "Hub IP address", description: "", required: true)
input("systemSecret", "string", title: "Hub Secret", required: true)
input("heatingBoost", "number", title: "Minutes for heating boost", required: false, defaultValue: 30)
input("waterBoost", "number", title: "Minutes for hot water boost", required: false, defaultValue: 60)
input("showDebug","bool", title: "Show debugging info", required: true, defaultValue: false)
}
}
}

def showDebugInfo() {
return showDebug
}

def test(dni = null) {
if (showDebug) log.debug "smartapp test($dni)"
//refreshAllChildren()
state.action = "test"
getHubUrl("/data/v2/domain/")
}

def refreshHub(dni) {
if (showDebug) log.debug "refreshHub()"
if (dni == null) dni = app.id + ":HUB"
def child = getChildDevice(dni)
//log.debug state.hubConfig.System.EcoModeEnabled
//log.debug state.hubConfig.System.OverrideType
//log.debug state.hubConfig.System.ComfortModeEnabled
child.setMode(state.hubConfig.System.OverrideType)
child.setEco(state.hubConfig.System.EcoModeEnabled)
child.setComfort(state.hubConfig.System.ComfortModeEnabled)
}

def refreshChild(dni) {
if (showDebug) log.debug "refreshChild($dni)"
def roomId = dni.split(":")[1]
def child = getChildDevice(dni)
def roomStatId
//log.debug roomId

if (roomId == "HW") {
	//if (showDebug) log.debug "Update Hot Water"
    def hotWater = state.hubConfig.HotWater[0]
    child.setState(hotWater.WaterHeatingState)
    child.setMode(hotWater.Mode)
    if (hotWater.OverrideTimeoutUnixTime) {
    	child.setBoost("On")
    } else {
    	child.setBoost("Off")
    }
} else {
	//if (showDebug) log.debug "Update room id{$roomId}"
    def rooms = state.hubConfig.Room
    rooms.each { room ->
        	if (roomId == room.id.toString()) {
            	
                child.setTemp(room.CalculatedTemperature/10, room.CurrentSetPoint/10)
                child.setMode(room.Mode)
                //child.setOutputState(room.ControlOutputState)
                child.setDemand(room.PercentageDemand)
                child.setWindowState(room.WindowState)
                if (room.OverrideTimeoutUnixTime) {
                	child.setBoost("On")
                } else {
                	child.setBoost("Off")
                }
                roomStatId = room.RoomStatId
                if (roomStatId) child.setHumidity(getHumidity(roomStatId))
            }
        }
}

}

def refreshAllChildren() {
if (showDebug) log.debug "refreshAllChildren()"
def children = getChildDevices()

children.each { child ->
		
		refreshChild(child.deviceNetworkId)
    
}

}

def getHubConfig() {
if (showDebug) log.debug "getHubConfig()"

def result = new hubitat.device.HubAction(
	method: "GET",
    path: "/data/domain/",
    headers: [
    	HOST: hubIP+":80",
        SECRET: systemSecret
    ],
    null,
    [callback: calledBackHandler]
    )
state.action = "Hub Config"
sendHubCommand(result)

}

def getHubUrl(path) {
if (showDebug) log.debug "getHubUrl($path)"

def result = new hubitat.device.HubAction(
	method: "GET",
    path: path,
    headers: [
    	HOST: hubIP+":80",
        SECRET: systemSecret
    ],
    null,
    [callback: calledBackHandler]
    )
//state.action = "Hub Config"
sendHubCommand(result)

}

def sendMessageToHeatHub(path, method, content) {
if (showDebug) log.debug "sendMessageToHeatHub($path, $method, $content)"

def result = new hubitat.device.HubAction(
method: method,
path: path,
headers: [
HOST: hubIP+":80",
SECRET: systemSecret
],
body: content,
null,
[callback: calledBackHandler]
)
//state.action = "Hub Config"
sendHubCommand(result)
}

void calledBackHandler(hubitat.device.HubResponse hubResponse) {
if (showDebug) log.debug "Entered calledBackHandler()..."
if (showDebug) log.debug hubResponse.status
//if (showDebug) log.debug "entering action: " + state.action

if (state.action == "test") log.debug hubResponse.json

if (state.action == "Hub Config") {
	state.hubConfig = hubResponse.json
    
	def rooms = state.hubConfig.Room   

	if (state.hubConfig.HotWater) {
	  	if (showDebug) log.debug "Got hot water"
	    createChildDevices(rooms, true)
	} else {
		if (showDebug) log.debug "No hot water"
		createChildDevices(rooms, false)
	}


}

if (state.action == "refreshChildren" ) {
	state.hubConfig = hubResponse.json
    refreshAllChildren()
}
	

if (hubResponse.status == 200) {
	if (state.action.contains("setPoint")) runIn(3, "getHubConfig")
	if (state.action == "ecoOn") getChildDevice(app.id + ":HUB").setEco(true)
	if (state.action == "ecoOff") getChildDevice(app.id + ":HUB").setEco(false)
    if (state.action == "comfortOn") getChildDevice(app.id + ":HUB").setComfort(true)
    if (state.action == "comfortOff") getChildDevice(app.id + ":HUB").setComfort(false)
    if (state.action.contains("changeroomManualMode")) {
    	def roomId = state.action.split(":")[1]
    	getChildDevice(app.id + ":$roomId").setMode("Manual")
        state.action = ""
    }
    if (state.action.contains("changeroomAutoMode")) {
    	def roomId = state.action.split(":")[1]
    	getChildDevice(app.id + ":$roomId").setMode("Auto")
        state.action = ""
    }
    if (state.action.contains("roomManualMode")) state.action = "change" + state.action
    if (state.action.contains("roomAutoMode")) state.action = "change" + state.action
    
    if (state.action == "hwManualChange") getChildDevice(app.id + ":HW").setMode("Manual")
    if (state.action == "hwAutoChange") getChildDevice(app.id + ":HW").setMode("Auto")
    if (state.action == "hwManual") state.action = "hwManualChange"
    if (state.action == "hwAuto") state.action = "hwAutoChange"
    
    if (state.action == "hwBoostOn") {
    	getChildDevice(app.id + ":HW").setBoost("on")
        getChildDevice(app.id + ":HW").setState("on")
        }
    if (state.action == "hwBoostOff") {
    	getChildDevice(app.id + ":HW").setBoost("off")
        getChildDevice(app.id + ":HW").setState("off")
        }
    if (state.action == "HotWaterOn") getChildDevice(app.id + ":HW").setState("on")
    if (state.action == "HotWaterOff") getChildDevice(app.id + ":HW").setState("off")
    
    if (state.action == "roomBoostOn") {
    	state.action = "refreshChildren"
        getHubUrl("/data/domain/")
    }
    if (state.action == "roomBoostOff") {
    	state.action = "refreshChildren"
        getHubUrl("/data/domain/")
    }
    
}
if (hubResponse.status == 403) {
	if (state.action == "homeModeChange") {
    	getChildDevice(app.id + ":HUB").setMode("Home")
        state.action = "refreshChildren"
        getHubUrl("/data/domain/")
        
    }
    if (state.action == "awayModeChange") {
    	getChildDevice(app.id + ":HUB").setMode("Away")
        state.action = "refreshChildren"
        getHubUrl("/data/domain/")
    }
    if (state.action == "homeMode") state.action = "homeModeChange"
    if (state.action == "awayMode") state.action = "awayModeChange"
	
    
    
}

//if (showDebug) log.debug "exiting action " + state.action

}

private void createChildDevices(rooms, hotwater) {
if (showDebug) log.debug "createChildDevices()"
def children = getChildDevices().deviceNetworkId
def child
def dni

dni = app.id + ":HUB"
if (children.contains(dni)) {
	if (showDebug) log.debug "Device ${dni} already exists"
    refreshHub(dni)
} else {
	try {
    	childDevice = addChildDevice("colc1705", "Drayton Wiser Hub", dni, null, ["label": "Drayton Hub"]) 
        refreshHub(dni)
    } catch (e) {
    	if (showDebug) log.debug "Error creating child device ${e}"
    }
}


for (HashMap room : rooms) {
	def dh
	dni = app.id + ":" + room.id
    if (children.contains(dni)) {
    	if (showDebug) log.debug "Device ${dni} already exists"
        refreshChild(dni)
    } else {
    	try {
        	if (room.RoomStatId) dh = "Drayton Wiser Room"
            if (room.SmartValveIds) dh = "Drayton Wiser TRV"
            childDevice = addChildDevice( "colc1705",dh, "$dni", null, ["label": "Drayton (${room.Name})"])
            //child.setTemp(room.CalculatedTemperature/10, room.CurrentSetPoint/10)
            refreshChild(dni)
    	} catch(e) {
    		if (showDebug) log.debug "Error creating child device ${e}"
    	}
    }
}

//add hot water
if (hotwater) {
	dni = app.id + ":HW"
    if (children.contains(dni)) {
    	if (showDebug) log.debug "Device ${dni} already exists"
        refreshChild(dni)
    } else {
		try {
            child = addChildDevice(app.namespace, "colc1705", "$dni", null, ["label": "Drayton Hot Water"])
            //child.setState(state.json.HotWater.WaterHeatingState)
            refreshChild(dni)
		} catch(e) {
			if (showDebug) log.debug "Error creating child device ${e}"
		}
    }
}

}

void setPoint(dni, setPoint) {
if (showDebug) log.debug "setPoint($dni, $setPoint)"
def roomId = dni.split(":")[1]
//if (showDebug) log.debug roomId
def newSP = setPoint * 10
if (roomId == "HW" ) {
if (showDebug) log.debug "This is the hotwater"

} else {
	state.action = "setPoint:" + roomId
    def payload
    payload = "{\"RequestOverride\":{\"Type\":\"Manual\", \"SetPoint\":" + newSP.toInteger().toString() + "}}"
    sendMessageToHeatHub(getRoomsEndpoint() + roomId.toString(), "PATCH", payload)
}

}

def setAwayMode(awayMode) {
if (showDebug) log.debug "setAwayMode($awayMode)"
def payload
def payload2
payload = "{"Type":" + (awayMode ? "2" : "0") + ","Originator": "App", "setPoint":" + (awayMode ? "50" : "0") + "}"
payload2 = "{"Type":" + (awayMode ? "2" : "0") + ", "setPoint":" + (awayMode ? "-200" : "0") + "}"
state.action = (awayMode ? "awayMode" : "homeMode")
return [sendMessageToHeatHub(getSystemEndpoint() + "RequestOverride", "PATCH", payload), delayAction(1000), sendMessageToHeatHub(getHotwaterEndpoint() + "2/RequestOverride", "PATCH", payload2)]
}

def setHotWaterManualMode(manualMode) {
if (showDebug) log.debug "setHotWaterManualMode($manualMode)"
def payload
def payload2
payload = "{"Mode":"" + (manualMode ? "Manual" : "Auto") + ""}"
payload2 = "{"RequestOverride":{"Type":"None","Originator" :"App","DurationMinutes":0,"SetPoint":0}}"
state.action = (manualMode ? "hwManual" : "hwAuto")
return [sendMessageToHeatHub(getHotwaterEndpoint() + "2", "PATCH", payload), delayAction(1000), sendMessageToHeatHub(getHotwaterEndpoint() + "2", "PATCH", payload2)]
}

def setEcoMode(ecoMode) {
if (showDebug) log.debug "setEcoMode($ecoMode)"
def payload
payload = "{"EcoModeEnabled":" + ecoMode + "}";
state.action = (ecoMode ? "ecoOn" : "ecoOff")
sendMessageToHeatHub(getSystemEndpoint(), "PATCH", payload);
//refresh();
}

def setComfort(comfort) {
if (showDebug) log.debug "setComfort($comfort)"
def payload
payload = "$comfort";
state.action = (comfort ? "comfortOn" : "comfortOff")
sendMessageToHeatHub(getSystemEndpoint(), "PATCH", payload);
}

def getHumidity(roomStatId) {
if (showDebug) log.debug "getHumidity($roomStatId)"
for (HashMap roomStat : state.hubConfig.RoomStat) {
if (roomStatId.toString().equals(roomStat.id.toString())) {
return roomStat.MeasuredHumidity
} else {
return 0
}
}
}

def setRoomManualMode(dni, manualMode) {
if (showDebug) log.debug "setRoomManualMode($dni, $manualMode)"
def roomId = dni.split(":")[1]
def payload
def payload2
payload = "{"Mode":"" + (manualMode ? "Manual" : "Auto") + ""}"
payload2 = "{"RequestOverride":{"Type":"None","Originator" :"App","DurationMinutes":0,"SetPoint":0}}"
state.action = (manualMode ? "roomManualMode:$roomId" : "roomAutoMode:$roomId")
return [sendMessageToHeatHub(getRoomsEndpoint() + roomId.toString(), "PATCH", payload), delayAction(1000), sendMessageToHeatHub(getRoomsEndpoint() + roomId.toString(), "PATCH", payload)]
}

def setRoomBoost(dni, boostTime, temp) {
def roomId = dni.split(":")[1]
def payload
if (boostTime == 0) {
state.action = "roomBoostOff"
payload = "{"RequestOverride":{"Type":"None","Originator":"App","DurationMinutes":0,"SetPoint":0}}"
} else {
boostTime = heatingBoost
state.action = "roomBoostOn"
payload = "{"RequestOverride":{"Type":"Manual","Originator":"App", "DurationMinutes":" + boostTime + ", "SetPoint":"+ (temp * 10).toInteger().toString() + "}}"
}
if (showDebug) log.debug "setRoomBoost($dni, $boostTime, $temp)"
sendMessageToHeatHub(getRoomsEndpoint() + roomId.toString(), "PATCH", payload)
}

def setHotWaterBoost(boostTime) {
def payload
if (boostTime == 0) {
state.action = "hwBoostOff"
payload = "{"RequestOverride":{"Type":"None","Originator":"App","DurationMinutes":" + boostTime + ","SetPoint":0}}"
} else {
boostTime = waterBoost
state.action = "hwBoostOn"
payload = "{"RequestOverride":{"Type":"Manual","Originator":"App","DurationMinutes":" + boostTime + ","SetPoint":1100}}"
}
if (showDebug) log.debug "setHotWaterBoost($dni, $boostTime)"
sendMessageToHeatHub(getHotwaterEndpoint() + "2", "PATCH", payload)
}

def turnHotWaterOn() {
if (showDebug) log.debug "turnHotWateOn()"
def payload
state.action = "HotWaterOn"
payload = "{"RequestOverride":{"Type":"Manual","SetPoint":1100}}"
sendMessageToHeatHub(getHotwaterEndpoint() + "2", "PATCH", payload)
}

def turnHotWaterOff() {
if (showDebug) log.debug "turnHotWateOff()"
def payload
state.action = "HotWaterOff"
payload = "{"RequestOverride":{"Type":"Manual","SetPoint":-200}}"
sendMessageToHeatHub(getHotwaterEndpoint() + "2", "PATCH", payload)
}

private delayAction(long time) {
new hubitat.device.HubAction("delay $time")
}

def getDeviceEndpoint() {
return "/data/domain/Device/"
}

def getRoomstatsEndpoint() {
return "/data/domain/RoomStat/"
}

def getTRVsEndpoint() {
return "/data/domain/SmartValve/"
}

def getRoomsEndpoint() {
return "/data/domain/Room/"
}

def getSchedulesEndpoint() {
return "/data/domain/Schedule/";
}

def getHeatChannelsEndpoint() {
return "/data/domain/HeatingChannel/"
}

def getSystemEndpoint() {
return "/data/domain/System/"
}

def getSystemEndpointv2() {
return "/data/v2/domain/System/"
}

def getStationEndpoint() {
return "/data/network/Station/"
}

def getDomainEndpoint() {
return "/data/domain/"
}

def getHotwaterEndpoint() {
return "/data/domain/HotWater/"
}

metadata {
definition (name: "Drayton Wiser Hub", namespace: "colc1705", author: "Colin Chapman") {
capability "Actuator"
capability "Polling"
capability "Refresh"
capability "Thermostat Mode"
capability "Switch"

    attribute "eco", "string"
    attribute "mode", "string"
    attribute "comfort", "string"
    
    command "test"
    command "ecoOn"
    command "ecoOff"
    command "homeMode"
    command "awayMode"
    command "comfortOn"
    command "comfortOff"
}


    
   
	main(["mode"])
    details(["mode","eco","comfort"])//,"test", "refresh"])
    
    //Uncomment below for V1 tile layout
	//details(["thermostat", "mode_auto", "mode_manual", "mode_off", "heatingSetpoint", "heatSliderControl", "boost", "boostSliderControl", "refresh"])
}

def parse(description) {
logEvent("parse()")

}

def initialize() {
logEvent("Initializing")
//state.json
//state.action
}

def installed() {
logEvent("Executing installed()")
//createChildDevices()
//response(refresh() + configure())
}

def configure() {
logEvent("Executing configure()")

}

def refresh() {
logEvent("Executing refresh()")

}

def updated() {
logEvent("Executing updated()")

}

def test() {
logEvent("test()")
parent.test(device.deviceNetworkId)

}

def setEco(ecoMode) {
logEvent("setEco($ecoMode)")
if (ecoMode) {
sendEvent(name: "eco", value: "on")
} else {
sendEvent(name: "eco", value: "off")
}
}

def setMode(mode) {
logEvent("setMode($mode)")
if (mode == "Away") {
sendEvent(name: "mode", value: "away")
} else {
sendEvent(name: "mode", value: "home")
}

}

def setComfort(mode) {
logEvent("setComfort($comfort)")
if (mode) {
sendEvent(name: "comfort", value: "on")
} else {
sendEvent(name: "comfort", value: "off")
}

}

def ecoOn() {
logEvent("ecoOn()")
parent.setEcoMode(true)
}

def ecoOff() {
logEvent("ecoOff()")
parent.setEcoMode(false)
}

def comfortOn() {
logEvent("comfortOn()")
parent.setComfort(false)
}

def comfortOff() {
logEvent("comfortOff()")
parent.setComfort(true)
}

def homeMode() {
logEvent("homeMode()")
parent.setAwayMode(false)
}

def awayMode() {
logEvent("awayMode()")
parent.setAwayMode(true)
}

def logEvent(event) {
if (parent.showDebugInfo()) {
log.debug event
} else {
//log.debug "Logging disabled"
}
}