[Project Help] Hive Active Heating Driver

Hey all,

There is a Smart Things driver, but I have tried to get it working, but it constantly comes up with errors. @codahq suggested I post here to see if anyone can help.

Hive 3.0 Smart App

Heating Device Handler

I do not have a Hive to be able to look at this in detail... but can you post what errors? I just looked over it, made some cleanup (removing tiles mostly) and saved it without errors but I assume that are not talking about errors when saving it.

Here is what I saved, I did not change the author or anything much (just the version to help identify a change occured):

/**
 *  Hive Heating
 *
 *  Copyright 2015 Alex Lee Yuk Cheung
 *
 *  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.
 *
 *
 *  VERSION HISTORY
 *  25.02.2016
 *  v2.0 BETA - Initial Release
 *	v2.1 - Introducing button temperature control via improved thermostat multi attribute tile. More responsive temperature control. 
 *		   Improve Boost button behaviour and look.
 *	v2.1.1 - Tweaks to temperature control responsiveness
 *  v2.1.2 - Minor tweaks to main display
 *	v2.1.3 - Allow changing of boost interval amount in device settings.
 *  v2.1.4 - Allow changing of boost temperature in device settings.
 *	v2.1.5 - Option to disable Hive Heating Device for summer. Disable mode stops any automation commands from other smart apps reactivating Hive Heating.
 *	v2.1.5b - Bug fix when desired heat set point is null, control stops working.
 *  v2.1.5c - Fix multitile button behaviour that has changed since ST app 2.1.0. Add colour code to temperature reporting in activity feed.
 *	v2.1.5d - Fix blank temperature readings on Android ST app 
 *	v2.1.5e - Another attempt to fix blank temperature reading on Android.
 *	v2.1.5f - Allow decimal value for boost temperature. Changes to VALUE_CONTROL method to match latest ST docs.
 *	v2.1.5g - Changes to tile display for iOS app v2.1.2
 *
 *	10.09.2016
 *	v2.1.6 - Allow a maximum temperature threshold to be set.
 *	v2.1.6b - Added event for maximum temperature threshold breach.
 *
 *	30.05.2017
 *	v2.2 - Updated to use new Beekeeper API - Huge thanks to Tom Beech!
 *	v2.2b - Bug fix. Refresh bug prevents installation of Hive devices.
 *
 *	30.10.2017
 *	v3.0 - Version refactor to reflect BeeKeeper API update.
 *
 *	08.10.2017
 *	v3.1 - New Smartthing App compatability
 *
 * 07.26.2019
 *  v3.2 - Initial work at converting for hubitat
 */
preferences 
{
	input( "boostInterval", "number", title: "Boost Interval (minutes)", description: "Boost interval amount in minutes", required: false, defaultValue: 10 )
    input( "boostTemp", "decimal", title: "Boost Temperature (°C)", description: "Boost interval amount in Centigrade", required: false, defaultValue: 22, range: "5..32" )
    input( "maxTempThreshold", "decimal", title: "Max Temperature Threshold (°C)", description: "Set the maximum temperature threshold in Centigrade", required: false, defaultValue: 32, range: "5..32" )
	input( "disableDevice", "bool", title: "Disable Hive Heating?", required: false, defaultValue: false )
}

metadata {
	definition (name: "Hive Heating", namespace: "alyc100", author: "Alex Lee Yuk Cheung", ocfDeviceType: "oic.d.thermostat", mnmn: "SmartThings", vid: "SmartThings-smartthings-Z-Wave_Thermostat") {
		capability "Actuator"
		capability "Polling"
		capability "Refresh"
		capability "Temperature Measurement"
        capability "Thermostat"
		capability "Thermostat Heating Setpoint"
		capability "Thermostat Mode"
		capability "Thermostat Operating State"
        capability "Health Check"
        
        command "heatingSetpointUp"
		command "heatingSetpointDown"
        command "boostTimeUp"
		command "boostTimeDown"
        command "setThermostatMode"
        command "setHeatingSetpoint"
        command "setTemperatureForSlider"
        command "setBoostLength"
        command "boostButton"
	}
}

// parse events into attributes
def parse(String description) {
	log.debug "Parsing '${description}'"
	// TODO: handle 'temperature' attribute
	// TODO: handle 'heatingSetpoint' attribute
	// TODO: handle 'thermostatSetpoint' attribute
	// TODO: handle 'thermostatMode' attribute
	// TODO: handle 'thermostatOperatingState' attribute
}

def installed() {
	log.debug "Executing 'installed'"
    state.boostLength = 60
    state.desiredHeatSetpoint = 7
    sendEvent(name: "checkInterval", value: 10 * 60 + 2 * 60, data: [protocol: "cloud"], displayed: false)
}

void updated() {
	log.debug "Executing 'updated'"
    sendEvent(name: "checkInterval", value: 10 * 60 + 2 * 60, data: [protocol: "cloud"], displayed: false)
}

// handle commands
def setHeatingSetpoint(temp) {
	log.debug "Executing 'setHeatingSetpoint with temp $temp'"
	def latestThermostatMode = device.latestState('thermostatMode')
    
    if (temp < 5) {
		temp = 5
	}
	if (temp > 32) {
		temp = 32
	}
         
    if (settings.disableDevice == null || settings.disableDevice == false) {
    	//if thermostat is off, set to manual 
        def args
   		if (latestThermostatMode.stringValue == 'off') {
    		args = [
        		mode: "SCHEDULE", target: temp
            ]
		
    	} 
    	else {
    	// {"target":7.5}
    		args = [
        		target: temp
        	]               
    	}
    	def resp = parent.apiPOST("/nodes/heating/${device.deviceNetworkId}", args)    	
    }
    runIn(4, refresh)
}

def setBoostLength(minutes) {
	log.debug "Executing 'setBoostLength with length $minutes minutes'"
    if (minutes < 10) {
		minutes = 10
	}
	if (minutes > 240) {
		minutes = 240
	}
    state.boostLength = minutes
    sendEvent("name":"boostLength", "value": state.boostLength, displayed: true)
    refreshBoostLabel()  
}

def getBoostIntervalValue() {
	if (settings.boostInterval == null) {
    	return 10
    } 
    return settings.boostInterval.toInteger()
}

def getBoostTempValue() {
	if (settings.boostTemp == null) {
    	return "22"
    } 
    return settings.boostTemp
}

def getMaxTempThreshold() {
	if (settings.maxTempThreshold == null) {
    	return "32"
    } 
    return settings.maxTempThreshold
}

def boostTimeUp() {
	log.debug "Executing 'boostTimeUp'"
    //Round down result
    int boostIntervalValue = getBoostIntervalValue()
    def newBoostLength = (state.boostLength + boostIntervalValue) - (state.boostLength % boostIntervalValue)
	setBoostLength(newBoostLength)
}

def boostTimeDown() {
	log.debug "Executing 'boostTimeDown'"
    //Round down result
    int boostIntervalValue = getBoostIntervalValue()
    def newBoostLength = (state.boostLength - boostIntervalValue) - (state.boostLength % boostIntervalValue)
	setBoostLength(newBoostLength)
}

def boostButton() {
	log.debug "Executing 'boostButton'"
	setThermostatMode('emergency heat')
}

def setHeatingSetpointToDesired() {
	setHeatingSetpoint(state.newSetpoint)
}

def setNewSetPointValue(newSetPointValue) {
	log.debug "Executing 'setNewSetPointValue' with value $newSetPointValue"
	unschedule('setHeatingSetpointToDesired')
    state.newSetpoint = newSetPointValue
    state.desiredHeatSetpoint = state.newSetpoint
	sendEvent("name":"desiredHeatSetpoint", "value": state.desiredHeatSetpoint, displayed: false)
	log.debug "Setting heat set point up to: ${state.newSetpoint}"
    runIn(3, setHeatingSetpointToDesired)
}

def heatingSetpointUp(){
	log.debug "Executing 'heatingSetpointUp'"
	setNewSetPointValue(getHeatTemp().toInteger() + 1)
}

def heatingSetpointDown(){
	log.debug "Executing 'heatingSetpointDown'"
	setNewSetPointValue(getHeatTemp().toInteger() - 1)
}

def setTemperatureForSlider(value) {
	log.debug "Executing 'setTemperatureForSlider with $value'"
	setNewSetPointValue(value)  
}

def getHeatTemp() { 
	return state.desiredHeatSetpoint == null ? device.currentValue("heatingSetpoint") : state.desiredHeatSetpoint
}

def off() {
	setThermostatMode('off')
}

def heat() {
	setThermostatMode('heat')
}

def emergencyHeat() {
	log.debug "Executing 'boost'"
	
    def latestThermostatMode = device.latestState('thermostatMode')
    
    //Don't do if already in BOOST mode.
	if (latestThermostatMode.stringValue != 'emergency heat') {
		setThermostatMode('emergency heat')
    }
    else {
    	log.debug "Already in boost mode."
    }

}

def auto() {
	setThermostatMode('auto')
}

def setThermostatMode(mode) {
	if (settings.disableDevice == null || settings.disableDevice == false) {
		mode = mode == 'cool' ? 'heat' : mode
		log.debug "Executing 'setThermostatMode with mode $mode'"
    	def args = [
        		mode: "SCHEDULE"
            ]
    	if (mode == 'off') {
     		args = [
        		mode: "OFF"
            ]
    	} else if (mode == 'heat') {
        	//mode": "MANUAL", "target": 20
    		args = [
        		mode: "MANUAL", 
                target: 20
            ]
    	} else if (mode == 'emergency heat') {  
    		if (state.boostLength == null || state.boostLength == '')
        	{
        		state.boostLength = 60
            	sendEvent("name":"boostLength", "value": 60, displayed: true)
        	}
    		//"mode": "BOOST","boost": 60,"target": 22
			args = [
            	mode: "BOOST",
                boost: state.boostLength,
                target: getBoostTempValue()
        	]
   		}
    
    	def resp = parent.apiPOST("/nodes/heating/${device.deviceNetworkId}", args)
		mode = mode == 'range' ? 'auto' : mode    	
    }
    runIn(4, refresh)
}

def refreshBoostLabel() {
	def boostLabel = "Start\n$state.boostLength Min Boost"
    def latestThermostatMode = device.latestState('thermostatMode')  
    if (latestThermostatMode.stringValue == 'emergency heat' ) {
    	boostLabel = "Restart\n$state.boostLength Min Boost"
    }
    sendEvent("name":"boostLabel", "value": boostLabel, displayed: false)
}

def poll() {
	log.debug "Executing 'poll'"
	def currentDevice = parent.getDeviceStatus(device.deviceNetworkId)
	if (currentDevice == []) {
		return []
	}
    log.debug "$device.name status: $currentDevice"
        //Construct status message
        def statusMsg = ""
        
        //Boost button label
        if (state.boostLength == null || state.boostLength == '')
        {
        	state.boostLength = 60
            sendEvent("name":"boostLength", "value": 60, displayed: true)
        }
    	def boostLabel = "Start\n$state.boostLength Min Boost"
        
        // get temperature status
        def temperature = currentDevice.props.temperature
        def heatingSetpoint = currentDevice.state.target as Double
        
        //Check heating set point against maximum threshold value.
        log.debug "Maximum temperature threshold set to: " + getMaxTempThreshold()
        if ((getMaxTempThreshold() as BigDecimal) < (heatingSetpoint as BigDecimal))
        {
        	log.debug "Maximum temperature threshold exceeded. " + heatingSetpoint + " is higher than " + getMaxTempThreshold()
            sendEvent(name: 'maxtempthresholdbreach', value: heatingSetpoint, unit: "C", displayed: false)
        	//Force temperature threshold to Hive API.
        	def args = [
        		target: getMaxTempThreshold()
            ]               
    
    		parent.apiPOST("/nodes/heating/${device.deviceNetworkId}", args)   
            heatingSetpoint = String.format("%2.1f", getMaxTempThreshold())           
        }
        
        // convert temperature reading of 1 degree to 7 as Hive app does
        if (heatingSetpoint == "1.0") {
        	heatingSetpoint = "7.0"
        }
        sendEvent(name: 'temperature', value: temperature, unit: "C", state: "heat")
        sendEvent(name: 'heatingSetpoint', value: heatingSetpoint, unit: "C", state: "heat")
        sendEvent(name: 'coolingSetpoint', value: heatingSetpoint, unit: "C", state: "heat")
        sendEvent(name: 'thermostatSetpoint', value: heatingSetpoint, unit: "C", state: "heat", displayed: false)
        sendEvent(name: 'thermostatFanMode', value: "off", displayed: false)
        
        state.desiredHeatSetpoint = heatingSetpoint
        sendEvent("name":"desiredHeatSetpoint", "value": state.desiredHeatSetpoint, unit: "C", displayed: false)
        
        // determine hive operating mode
        def mode = currentDevice.state.mode.toLowerCase()
        
        //If Hive heating device is set to disabled, then force off if not already off.
        if (settings.disableDevice != null && settings.disableDevice == true && mode != "off") {
        	def args = [
        		mode: "OFF"
            ]
        	parent.apiPOST("/nodes/heating/${device.deviceNetworkId}", args)
            mode = 'off'
        } 
        else if (mode == "boost") {
        	mode = 'emergency heat'          
            def boostTime = currentDevice.state.boost
            boostLabel = "Restart\n$state.boostLength Min Boost"
            statusMsg = "Boost " + boostTime + "min"
            sendEvent("name":"boostTimeRemaining", "value": boostTime + " mins")
        }
        else if (mode == "manual") {
        	mode = 'heat'
            statusMsg = statusMsg + " Manual"
        }
        else if (mode == "off") {
        	mode = 'off'
            statusMsg = statusMsg + " Off"
        }
        else {
        	mode = 'auto'
        	statusMsg = statusMsg + " Schedule"
        }
        
        if (settings.disableDevice != null && settings.disableDevice == true) {
        	statusMsg = "DISABLED"
        }
        
        sendEvent(name: 'thermostatMode', value: mode) 
        
        // determine if Hive heating relay is on
        def stateHeatingRelay = (heatingSetpoint as BigDecimal) > (temperature as BigDecimal)
        
        log.debug "stateHeatingRelay: $stateHeatingRelay"
        
        if (stateHeatingRelay) {
        	sendEvent(name: 'thermostatOperatingState', value: "heating")
        }       
        else {
        	sendEvent(name: 'thermostatOperatingState', value: "idle")
        }  
               
        sendEvent("name":"hiveHeating", "value": statusMsg, displayed: false)  
        sendEvent("name":"boostLabel", "value": boostLabel, displayed: false)
     
}

def refresh() {
	log.debug "Executing 'refresh'"
	poll()
}

Here's a first try. Please let me know how far these get you.


I essentially just did what snell did and that's replace the obvious ones. I'm guessing there will be a few more changes.

I@codahq @snell

i've added a google link to my results. I managed to get into the username/password screen after changing some of the code myself, but none of the buttons work when adding a driver to the virtual device. So I stopped the test after 2 buttons

https://drive.google.com/file/d/1sj0MBBfMEFaFvJTSQfOiR0uCrHMfBKKG/view?usp=sharing

1 Like

@Shaneb:
Have you tried the Generic ZigBee Thermostat driver?

I’m not even sure how that would work. Great a virtual device and add generic zigbee thermostat?

How would that control Hive without it being linked

I was looking up information about the Hive thermostat, since I do not have one, and saw it was listed as having ZigBee on their website. So I thought it could be paired directly.

That appears to have been a bit mistaken because despite them listing connectivity as "ZigBee" it looks like they must be doing "other" stuff (at least according to people wanting to use it with smartthings) so it does not pair with anything but their own hubs. Ugh... (that stuff always bothers me).

Not sure how useful I can be then. Hopefully someone else with a Hive can chime in how they got it working.

Okay, good finds and good documentation as well. Do you work in QA?

Please try the updated app. I fixed the issues you reported. Also, please delete the device you created manually (I think you created the device manually based on the last two errors in your spreadsheet). The app needs to create your heating device otherwise it won't establish the parent/child relationship and the device won't be able to ask the parent app to query the cloud for it.

I have a feeling there will be some more issues once these are fixed but we're making good progress.

*edit: Oh, yeah. You for sure created it manually. I see it as one of your test steps. Don't do that. The app is supposed to create it.

Thanks I’ll give it a go. I don’t work in QA, I’m a business analyst so end up looking at test scripts, they are far better than my attempt

@codahq I have added a google sheet, so I can add new tabs each test.

https://drive.google.com/file/d/1sj0MBBfMEFaFvJTSQfOiR0uCrHMfBKKG/view?usp=sharing

Tab2 was more successful. I got the app, driver installed and log in credentials added and it recognised 1 heating device which i have selected.

I the selected the automatons button within the app, but I got the same 'child' error.

Out of curiosity I also created a virtual device selected Hive Heating Driver, which added all the tiles, but I got consistent errors with all the buttons. Let me know if you need me to screen shot them.

Yeah, if you create a virtual device manually it's not going to work at all. You need to let the app create the device. Can you tell me what happens in the log when you click on new automation?

Have you already gone into the devices list and clicked on/selected the heating device and then clicked on done? I believe that is where it creates the device. Please check if there are two devices using the driver; one you created through create virtual and one created by the app.

If there aren't, please try what I described. If a device is not created, please send what happens in the logs.

Ahh that's where i'm going wrong then. I have sent you an IM with a video of what's going on. I can see an error in the log though.

app:2942019-07-29 07:48:58.397 am errorcom.hubitat.app.exception.UnknownDeviceTypeException: Device type 'Hive Heating' in namespace 'null' not found on line 1027 (updated)

Okay, sounds like the device is creating now and button control works at least to some extent.

Let me know what you find in the logs when you click the automations link in the app now that the child device is present.

When clicking 'Hive automations Mode. it takes me to a new page

When I click that it displays this error

all thats in the logs is

Nice one, so far the only thing i've found that doesn't work is automations in the connect app, but i'll probably never use that as i'll use RM to make any automations.

Thank you SOOO much @codahq, i can finally turn off my smartthings hub!!!!

I think it’s being worked on

Sorry @Shaneb, I forgot to include you in my thank you as well.

Guess i got a bit over excited at being able to unplug the ST hub. lol.

Couple of things...

  1. As far as I can tell this doesn't seem like it would be working in SmartThings either. Does this work in ST? It's creating a reference to itself instead of the child app for automations.

  2. Where is the child app for automations? There is another app called "Hive Mode Automation" that I can't find in the repository you guys linked.

@codahq to be honest, i never used it in ST either so i couldn't tell you.

I have a basic schedule set up in Hive, and then used ST (now HE) to override the schedule depending if someone was home & the current inside & outside temps.

Andi

Okay, let's see if anybody else had tried that in ST. I'm very curious at this point to see if it did anything other than take you back to the app's main page.

I found an issue that would cause it to throw an error rather than do that. That is now fixed. When you click on the create automation link now it just sends you home instead of creating an automation. Since I couldn't even find the other app in the repository I'm kind of guessing that's what ST did as well. Like... Maybe the original developer gave out the automation child app to a handful of people and had them change this so they could test it. It says in the app automations is beta so maybe that's how it was working.

I dunno. At any rate, I don't see how this could have been working in ST unless it was modified.