Hive(connect) problem

Thanks brumster instructions were really useful for a noob such as myself. However I can't get past step 5, any ideas anybody please? I have checked and 2FA is off but I get this message when trying to authenticate.

Error: Unable to determine the current character, it is not a string, number, array, or object The current character read is 'P' with an int value of 80 Unable to determine the current character, it is not a string, number, array, or object line number 1 index number 0 PASTE HiveToken.json content into here ^

1 Like

The integration is broken atm following a change made by Hive to prevent access to the API. The guys on the ST forum appear to be getting somewhere but I think any success will be short lives when ST eventually pull the plug on the IDE. Solving this is beyond me so I'll sit here hopeful that some bright spark can solve the issue so that integration with HE works again at some point.

Seems like the ST are having some success.

Just tested this and it works :slight_smile:

So I've just had a go. All credit goes to the guys on the ST forum linked above. Here's what I did following the instructions on the ST forum, described here with related modifications to make it work on HE!

Install the Hive Connect app from here: https://raw.githubusercontent.com/alyc100/SmartThingsPublic/master/smartapps/alyc100/hive-connect.src/hive-connect.groovy

Then:

  1. In the App edit all references to 'app.namespace' and change it to "alyc100" , make sure you insert the alyc100 in quotes as above.
  2. Then on the next line of code replace:
    if(tmaApp) {
    with this:
    if(childApps) {

Install the driver:
https://raw.githubusercontent.com/alyc100/SmartThingsPublic/master/devicetypes/alyc100/hive-heating.src/hive-heating.groovy

And then comment out these two lines:
capability "tigerdrum36561.boostLabel"
capability "tigerdrum36561.boostLength"

Note: Removal of these lines likely to break the boost functionality! I'm not sure at the stage. If anyone else has a view on this let me know please.

Then do this:

  1. You need a computer where you can run node.js. I'm using a Mac, this took minutes with OSX, I can't comment for Windows!
  2. Install node.js if you haven’t already. https://nodejs.org/en/ , on the mac: brew install node.js
  3. Check nodejs is installed. In a terminal (or Command Line) run
    node -v
    you should see a version number appear which means it’s installed.
  4. Run ‘npm init’ and keep pressing ENTER to create a package.json file. You should type YES when prompted 'Is this OK? (yes) ’ to complete.
  5. Run ‘npm install amazon-user-pool-srp-client --save
  6. Run ’npm install axios --save
  7. Download index.js from https://www.dropbox.com/s/f9zphtaomgaqwoa/index.js?dl=0 and replace the index.js already present in ‘node_modules/amazon-user-pool-srp-client’ with downloaded one. On OS X, my node-modules folder was located in my Home directory.
  8. Edit the index.js and add your Hive user email and password at your end of the file and save. It should be obvious which lines you need to edit to do this.
  9. Set the environment for the index.js script:

OS X/UNIX/Linux - In the amazon-user-pool-srp-client directory run
export CognitoUserPoolUsers=eu-west-1_SamNfoWtf CognitoUserPoolClientWeb=3rl4i0ajrmtdm8sbre54p9dvd9’.

Windows Powershell - In the amazon-user-pool-srp-client directory run
$env:CognitoUserPoolUsers = ‘eu-west-1_SamNfoWtf’ and then
$env:CognitoUserPoolClientWeb = ‘3rl4i0ajrmtdm8sbre54p9dvd9’

  1. Run ‘node index.js > HiveTokens.json’ to generate your token file.

Now you need to paste the token values into the Hive(Connect) app.

  1. Open the HiveTokens.json file in a text editor.
  2. Edit the Hive app
  3. Intentionally blank
  4. Copy the contents of HiveTokens.js file and paste them into the ‘’ single quotes in line 109. It’s a very long string.
  5. Save and then add the app.
  6. Go to Apps and add the Hive App
  7. Hopefully it should say ‘AUTHENTICATED’ in the smart app.

Thanks again to @Baz2473 as well as the guys on ST forum. @Baz2473 if you have any ideas on these two lines let me know:

    capability "tigerdrum36561.boostLabel"
    capability "tigerdrum36561.boostLength"

Hi there,

Would you mind pasting a copy of the App code you are using, as there are 2 sections where if(tmaApp) { is referred to (Lines 183 and 322) - however when I try and run the app I get the following error:

Error: No signature of method: user_app_alyc100_Hive__Connect__289.findChildAppByName() is applicable for argument types: (org.codehaus.groovy.runtime.GStringImpl) values: [Hive (Connect)]

Many thanks,
Guy

Guy, I won't past the code just yet as it has my token in it.

Did you do this:

In the App edit all references to app.namespace and change it to "alyc100"

Make sure you post the alyc100 in quotes as above.

I only updated the first instance of this:

if(tmaApp) {
with this:
if(childApps) {

The 2nd relates to automations which I don't use) but I've just updated it any way. Good spot!

Hi there,

I'm still having an issue running the App - are you able to paste a copy of the code obfuscating your token please (or just completely excluding line 109)

Many thanks,
Guy

Odd the token is not present on line 109 when I look at the app code, it must remove it from the hub once the app is running??????

Try this:

/**
 *  Hive (Connect)
 *
 *  Copyright 2015,2016 Alex Lee Yuk Cheung
 *	Hive Contact Sensor code portions contributed by Simon Green
 *  Hive Active Bulb code portions contributed by Tom Beech
 *
 *  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
 *
 *  24.02.2016
 *  v2.0 BETA - New Hive Connect App
 *  v2.0.1 BETA - Fix bug for accounts that do not have capabilities attribute against thermostat.
 *	v2.1 - Improved authentication process and overhaul to UI. Added notification capability.
 *  v2.1.1 - Bug fix when initially selecting devices for the first time.
 *	v2.1.2 - Move external icon references into Github\
 *
 *	17.08.2016
 *  v2.1.3 - Fix null pointer on state variable corruption
 *	v2.1.3b - Fix device failure on API timeout
 *
 *	01.09.2016
 *	v2.2 - Integrate auto mode functionality from Auto Mode for Thermostat smart app
 *
 *  04.09.2016
 *	v2.3 - Added support for Hive Contact Sensor - Author: Simon Green
 *
 *	06.09.2016
 *	v2.3.1 - Improve device detection
 *
 *	10.09.2016
 *	v2.3.2 - Added notification option for maximum temperature threshold breach for Hive heating devices.
 *
 *  23.1.2016
 *  v2.4 - Added support for Hive Active Warm White and Hive Active Tunable Lights - Author: Tom Beech
 *	v2.4b - Minor UI fixes for bulb devices.
 *
 *  28.11.2016 
 *  v2.5 - Added support for Hive Active Plugs - Author: Tom Beech
 *		 - Refactor of device selecting string - Author: Tom Beech
 *		 - Review device naming and text consistency.
 *
 *	v2.5b - Shortern some device names.
 *
 * 	02.12.2016
 * 	v2.6 - Added support for Hive Active Colour Bulb - Author: Tom Beech
 *
 *	28.05.2017
 *	v2.7 - Support for new Hive Beekeeper API - Authors: Tom Beech, Alex Lee Yuk Cheung
 *		 - Removed support for Hive Contact Sensor. Zigbee integration by Simon Green is preferred option.
 *	v2.7b - Bug fix. Refresh bug prevents installation of Hive devices.
 *
 *	30.10.2017
 *	v3.0 - Support for Hive Active Light Colour Tuneable device.
 *
 *  4.10.2019
 *  v3.1 - Support for Hive Radiator TRV - Authors: Ben Lee
 *
 *  13.10.2020
 *  v3.1.1b - Fix device suffix being set within deviceId
 *
 *  05.12.2020
 *  v3.2 - Change authentication method to use Tokens generated from amazon-user-pool-srp-client
 *
 *  06.12.2020
 *  v3.2a - Reduce token refresh frequency
 *  v3.2b - Add Bearer token on API call
 *
 *  07.12.2020
 *  v3.2c - Remove username and password references that are now redundant
 */
definition(
		name: "Hive (Connect)",
		namespace: "alyc100",
		author: "Alex Lee Yuk Cheung",
		description: "Connect your Hive devices to SmartThings.",
		iconUrl: "https://raw.githubusercontent.com/alyc100/SmartThingsPublic/master/smartapps/alyc100/10457773_334250273417145_3395772416845089626_n.png",
		iconX2Url: "https://raw.githubusercontent.com/alyc100/SmartThingsPublic/master/smartapps/alyc100/10457773_334250273417145_3395772416845089626_n.png",
    singleInstance: true
)

preferences {
	//startPage
	page(name: "startPage")

	//Connect Pages
	page(name:"mainPage", title:"Hive Device Setup", content:"mainPage", install: true)
	page(name: "loginPAGE")
	page(name: "selectDevicePAGE")
	page(name: "preferencesPAGE")
	page(name: "tmaPAGE")

	//Thermostat Mode Automation Pages
	page(name: "tmaConfigurePAGE")
}

def apiBeekeeperUKURL(path = '/') 			 { return "https://beekeeper-uk.hivehome.com:443/1.0${path}" }
def apiBeekeeperURL(path = '/') 			 { return "https://beekeeper.hivehome.com:443/1.0${path}" }

def initTokens() {
	def hiveTokenString = 'PASTE HiveToken.json content into here'
	return new groovy.json.JsonSlurper().parseText(hiveTokenString)
}

def startPage() {
	if (parent) {
		atomicState?.isParent = false
		tmaConfigurePAGE()
	} else {
		atomicState?.isParent = true
		mainPage()
	}
}

//Hive Connect App Pages

def mainPage() {
	log.debug "mainPage"
	if (!state.beekeeperToken || state.beekeeperToken == '') {
		return dynamicPage(name: "mainPage", title: "", install: true, uninstall: true) {
			section {
				headerSECTION()
				href("loginPAGE", title: null, description: authenticated() ? "AUTHENTICATED. Tap to refresh authentication" : "Tap to refresh authentication", state: authenticated())
			}
		}
	} else {
		log.debug "next phase"
		return dynamicPage(name: "mainPage", title: "", install: true, uninstall: true) {
			section {
				headerSECTION()
				href("loginPAGE", title: "Authentication", description: authenticated() ? "AUTHENTICATED. Tap to refresh authentication" : "Tap to refresh authentication", state: authenticated())
			}
			if (stateTokenPresent()) {
				section ("Choose your devices:") {
					href("selectDevicePAGE", title: "Devices", description: devicesSelected() ? getDevicesSelectedString() : "Tap to select devices", state: devicesSelected())
				}
				section("Hive Mode Automations:") {
					href "tmaPAGE", title: "Hive Mode Automations...", description: (tmaDescription() ? tmaDescription() : "Tap to Configure..."), state: (tmaDescription() ? "complete" : null)
				}
				section ("Notifications:") {
					href("preferencesPAGE", title: null, description: preferencesSelected() ? getPreferencesString() : "Tap to configure notifications", state: preferencesSelected())
				}
			} else {
				section {
					paragraph "NOT AUTHENTICATED. There was a problem connecting to Hive. Check your generated token and error logs in SmartThings web console.\n\n${state.loginerrors}"
				}
			}
		}
	}
}

def headerSECTION() {
	return paragraph (image: "https://raw.githubusercontent.com/alyc100/SmartThingsPublic/master/smartapps/alyc100/10457773_334250273417145_3395772416845089626_n.png",
                  "Hive (Connect)\nVersion: v3.2c\nDate: 07122020(1500)")
}

def stateTokenPresent() {
	return state.beekeeperToken != null && state.beekeeperToken != ''
}

def authenticated() {
	return (state.beekeeperToken != null && state.beekeeperToken != '') ? "complete" : null
}

def devicesSelected() {
	return (selectedHeating || selectedHotWater || selectedBulb || selectedTunableBulb || selectedActivePlug || selectedColourBulb) ? "complete" : null
}

def preferencesSelected() {
	return (sendPush || sendSMS != null) && (maxtemp != null || mintemp != null || sendBoost || sendOff || sendManual || sendSchedule || sendMaxThresholdBreach) ? "complete" : null
}

def tmaDescription() {
//	def tmaApp = findChildAppByName( appName() )
//	if(tmaApp) {
    if(childApps) {
		def str = ""
		str += "Thermostat Automations:"

		childApps?.each { a ->
			def name = a?.getLabel()
			str += "\n• $name"
		}

		return str
	}
	return null
}


def getDevicesSelectedString() {
	if (state.hiveHeatingDevices == null ||
    	state.hiveHotWaterDevices == null || 
        state.hiveTunableBulbDevices == null || 
        state.hiveBulbDevices == null ||
        state.hiveActivePlugDevices == null ||
        state.hiveColourBulb == null) {
    	updateDevices()
  }
	def listString = ""
    
	selectedHeating.each { childDevice ->    
    	if (null != state.hiveHeatingDevices)
    		listString += "${state.hiveHeatingDevices[childDevice]}\n"
    }
  
	selectedHotWater.each { childDevice ->
      if (null != state.hiveHotWaterDevices) 
           	listString += "${state.hiveHotWaterDevices[childDevice]}\n"
	}
    
	selectedBulb.each { childDevice ->
        if (null != state.hiveBulbDevices)
            listString += "${state.hiveBulbDevices[childDevice]}\n"
	}
    
	selectedTunableBulb.each { childDevice ->		
		if (null != state.hiveTunableBulbDevices)
            listString += "${state.hiveTunableBulbDevices[childDevice]}\n"
	}    
    selectedActivePlug.each { childDevice ->		
		if (null != state.hiveActivePlugDevices)
            listString += "${state.hiveActivePlugDevices[childDevice]}\n"
	}
    selectedColourBulb.each {  childDevice ->		
		if (null != state.selectedColourBulb)
            listString += "${state.selectedColourBulb[childDevice]}\n"
	}
  
  	// Returns the completed list, and trims the last carrige return
	return listString.trim()
}


def getPreferencesString() {
	def listString = ""
  if (sendPush) listString += "Send Push, "
  if (sendSMS != null) listString += "Send SMS, "
  if (maxtemp != null) listString += "Max Temp: ${maxtemp}, "
  if (mintemp != null) listString += "Min Temp: ${mintemp}, "
  if (sendBoost) listString += "Boost, "
  if (sendOff) listString += "Off, "
  if (sendManual) listString += "Manual, "
  if (sendSchedule) listString += "Schedule, "
  if (sendMaxThresholdBreach) listString += "Max Temp Threshold Breach, "
  if (listString != "") listString = listString.substring(0, listString.length() - 2)
  return listString
}

def loginPAGE() {
	getBeekeeperAccessToken()
	if (!state.beekeeperToken || state.beekeeperToken == '') {
		return dynamicPage(name: "loginPAGE", title: "Login", uninstall: false, install: false) {
			section { headerSECTION() }
			section { paragraph "NOT AUTHENTICATED. There was a problem connecting to Hive. Check your generated token and error logs in SmartThings web console.\n\n${state.loginerrors}" }
		}
	} else {
		dynamicPage(name: "loginPAGE", title: "Login", uninstall: false, install: false) {
			section { headerSECTION() }
			if (stateTokenPresent()) {
				section {
					paragraph "AUTHENTICATED. You have successfully connected to Hive. Click 'Next' to go back to the main screen and choose your Hive devices."
				}
			} else {
				section {
					paragraph "NOT AUTHENTICATED. There was a problem connecting to Hive. Check your generated token and error logs in SmartThings web console.\n\n${state.loginerrors}"
				}
			}
		}
	}
}

def selectDevicePAGE() {
	updateDevices()
	dynamicPage(name: "selectDevicePAGE", title: "Devices", uninstall: false, install: false) {
  	section { headerSECTION() }
    	section("Select your devices:") {
			input "selectedHeating", "enum", image: "https://raw.githubusercontent.com/alyc100/SmartThingsPublic/master/smartapps/alyc100/thermostat-frame-6c75d5394d102f52cb8cf73704855446.png", required:false, title:"Select Hive Heating Devices \n(${state.hiveHeatingDevices.size() ?: 0} found)", multiple:true, options:state.hiveHeatingDevices
			input "selectedHotWater", "enum", image: "https://raw.githubusercontent.com/alyc100/SmartThingsPublic/master/smartapps/alyc100/thermostat-frame-6c75d5394d102f52cb8cf73704855446.png", required:false, title:"Select Hive Hot Water Devices \n(${state.hiveHotWaterDevices.size() ?: 0} found)", multiple:true, options:state.hiveHotWaterDevices
            input "selectedBulb", "enum", image: "https://raw.githubusercontent.com/alyc100/SmartThingsPublic/master/smartapps/alyc100/hive-bulb.jpg", required:false, title:"Select Hive Light Dimmable Devices \n(${state.hiveBulbDevices.size() ?: 0} found)", multiple:true, options:state.hiveBulbDevices
			input "selectedTunableBulb", "enum", image: "https://raw.githubusercontent.com/alyc100/SmartThingsPublic/master/smartapps/alyc100/hive-tunablebulb.jpg", required:false, title:"Select Hive Light Tuneable Devices \n(${state.hiveTunableBulbDevices.size() ?: 0} found)", multiple:true, options:state.hiveTunableBulbDevices
            input "selectedColourBulb", "enum", image: "https://raw.githubusercontent.com/alyc100/SmartThingsPublic/master/smartapps/alyc100/hive-colouredbulb.jpg", required:false, title:"Select Hive Light Colour Devices \n(${state.hiveColourBulb.size() ?: 0} found)", multiple:true, options:state.hiveColourBulb
            input "selectedActivePlug", "enum", image: "https://raw.githubusercontent.com/alyc100/SmartThingsPublic/master/smartapps/alyc100/hive-activeplug.jpg", required:false, title:"Select Hive Plug Devices \n(${state.hiveActivePlugDevices.size() ?: 0} found)", multiple:true, options:state.hiveActivePlugDevices
		}
  	}
}

def preferencesPAGE() {
	dynamicPage(name: "preferencesPAGE", title: "Preferences", uninstall: false, install: false) {
    section {
    	input "sendPush", "bool", title: "Send as Push?", required: false, defaultValue: false
			input "sendSMS", "phone", title: "Send as SMS?", required: false, defaultValue: null
    }
    section("Thermostat Notifications:") {
			input "sendBoost", "bool", title: "Notify when mode is Boosting?", required: false, defaultValue: false
			input "sendOff", "bool", title: "Notify when mode is Off?", required: false, defaultValue: false
			input "sendManual", "bool", title: "Notify when mode is Manual?", required: false, defaultValue: false
      input "sendSchedule", "bool", title: "Notify when mode is Schedule?", required: false, defaultValue: false
		}
    section("Thermostat Max Temperature") {
    	input ("maxtemp", "number", title: "Alert when temperature is above this value", required: false, defaultValue: 25)
    }
    section("Thermostat Min Temperature") {
    	input ("mintemp", "number", title: "Alert when temperature is below this value", required: false, defaultValue: 10)
    }
    section("Thermostat Max Threshold Breach") {
    	input "sendMaxThresholdBreach", "bool", title: "Notify when max temp threshold has been breached?", required: false, defaultValue: false
    }
  }
}

def tmaPAGE() {
	dynamicPage(name: "tmaPAGE", title: "", nextPage: !parent ? "startPage" : "tmaPAGE", install: false) {
		def tmaApp = findChildAppByName( appName() )
//		if(tmaApp) {
        if(childApps) {
			section("Configured Hive Mode Automations...") { }
		} else {
			section("") {
				paragraph "Create New Hive Mode Automation to get Started..."
			}
		}
		section("Add a new Automation:") {
			app(name: "tmaApp", appName: appName(), namespace: "alyc100", multiple: true, title: "Create New Mode Automation...")
			def rText = "NOTICE:\nIntegrated Hive Mode Automations is in BETA\n"
			paragraph "${rText}"//, required: true, state: null
		}
	}
}

//Auto Thermostat Mode Pages
def tmaConfigurePAGE() {
	dynamicPage(name: "tmaConfigurePAGE", title: "Hive Mode Automation", install: true, uninstall: true) {
		section {
    		input ("thermostats", "capability.thermostat", title: "For these thermostats",  multiple: true, required: true)
  	}
    section {
        input(name: "modeTrigger", title: "Set the trigger to",
              description: null, multiple: false, required: true, submitOnChange: true, type: "enum",
              options: ["true": "Mode Change", "false": "Switches"])
    }
    if (modeTrigger == "true") {
      // Do something here like update a message on the screen,
      // or introduce more inputs. submitOnChange will refresh
      // the page and allow the user to see the changes immediately.
      // For example, you could prompt for the level of the dimmers
      // if dimmers have been selected:
      section {
 				input ("modes", "mode", title:"When SmartThings enters these modes", multiple: true, required: true)
			}
    } else if (modeTrigger == "false") {
    	section {
    		input ("theSwitch", "capability.switch", title:"When this switch is activated", multiple: false, required: true)
      }
    }
		section {
			input ("alteredThermostatMode", "enum", multiple: false, title: "Set thermostats to this mode",
             options: ["Set To Schedule", "Boost", "Turn Off", "Set to Manual"], required: true, defaultValue: 'Turn Off')
		}
    section {
    	input ("resetThermostats", "enum", title: "Reset thermostats after trigger turns off?",
             options: ["true": "Yes","false": "No"], required: true, submitOnChange: true)
  	}
    if (resetThermostats == "true") {
      section {
			input ("resumedThermostatMode", "enum", multiple: false, title: "Reset thermostats back to this mode", submitOnChange: true,
            	options: ["Set To Schedule", "Boost", "Turn Off", "Set to Manual"], required: true, defaultValue: 'Set To Schedule')
		  }
      if (resumedThermostatMode == "Boost") {
      	section {
					input ("thermostatModeAfterBoost", "enum", multiple: false, title: "What to do when Boost has finished",
        	 				options: ["Set To Schedule", "Turn Off", "Set to Manual"], required: true, defaultValue: 'Set To Schedule')
				}
      }
    }
		section( "Additional configuration" ) {
      input ("days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
		         options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"])
      href "timeIntervalInput", title: "Only during a certain time", description: getTimeLabel(starting, ending), state: greyedOutTime(starting, ending), refreshAfterSelection:true
		  input ("temp", "decimal", title: "If setting to Manual, set the temperature to this", required: false, defaultValue: 21)
    }
		section( "Notifications" ) {
    	input ("sendPushMessage", "enum", title: "Send a push notification?",
             options: ["Yes", "No"], required: true)
    	input ("phone", "phone", title: "Send a Text Message?", required: false)
		}
    section {
    	label title: "Assign a name", required: true
  	}
	}
}

page(name: "timeIntervalInput", title: "Only during a certain time", refreshAfterSelection:true) {
	section {
		input "starting", "time", title: "Starting", required: false
		input "ending", "time", title: "Ending", required: false
	}
}


// App lifecycle hooks

def installed() {
	if(parent) { installedChild() } // This will handle all of the install functions when the child app is installed
	else { installedParent() } // This will handle all of the install functions when the parent app is installed
}

def updated() {
	if(parent) { updatedChild() } // This will handle all of the install functions when the child app is updated
	else { updatedParent() } // This will handle all of the install functions when the parent app is updated
}

def uninstalled() {
	if(parent) { } // This will handle all of the install functions when the child app is uninstalled
	else { uninstalledParent() } // This will handle all of the install functions when the parent app is uninstalled
}

def installedParent() {
	log.debug "installed"
	initialize()
	// Check for new devices every 3 hours
	runEvery3Hours('updateDevices')
	// execute handlerMethod every 10 minutes.
	runEvery10Minutes('refreshDevices')
}

// called after settings are changed
def updatedParent() {
	log.debug "updated"
	unsubscribe()
	initialize()
	unschedule('refreshDevices')
	runEvery10Minutes('refreshDevices')
}

def uninstalledParent() {
	log.info("Uninstalling, removing child devices...")
	unschedule()
	removeChildDevices(getChildDevices())
}

private removeChildDevices(devices) {
	devices.each {
		deleteChildDevice(it.deviceNetworkId) // 'it' is default
	}
}

def installedChild() {
  log.debug "Installed with settings: ${settings}"
  //set up initial thermostat state and force thermostat into correct mode
  state.thermostatAltered = false
  state.boostingReset = false

  //Flags to stop possible infinite loop scenarios when handlers create events
  state.internalThermostatEvent = false
  state.internalSwitchEvent = false

  subscribe(thermostats, "thermostatMode", thermostateventHandlerForTMA, [filterEvents: false])
  //Check if mode or switch is the trigger and run initialisation
  if (modeTrigger == "true") {
  	def currentMode = location.mode
  	log.debug "currentMode = $currentMode"
  	if (currentMode in modes) {
      	takeActionForMode(currentMode)
  	}
  	subscribe(location, "mode", modeeventHandlerForTMA, [filterEvents: false])
  }
  else {
  	if (theSwitch.currentSwitch == "on") {
      	takeActionForSwitch(theSwitch.currentSwitch)
      }
  	subscribe(theSwitch, "switch", switcheventHandlerForTMA, [filterEvents: false])
  }
}

def updatedChild() {
  log.debug "Updated with settings: ${settings}"
  unsubscribe()
  //set up initial thermostat state and force thermostat into correct mode
  state.thermostatAltered = false
  state.boostingReset = false
  state.internalThermostatEvent = false
  state.internalSwitchEvent = false
  subscribe(thermostats, "thermostatMode", thermostateventHandlerForTMA, [filterEvents: false])
  //Check if mode or switch is the trigger and run initialisation
  if (modeTrigger == "true") {
  	def currentMode = location.mode
  	log.debug "currentMode = $currentMode"
  	if (currentMode in modes) {
      	takeActionForMode(currentMode)
  	}
  	subscribe(location, "mode", modeeventHandlerForTMA, [filterEvents: false])
  } else {
  	if (theSwitch.currentSwitch == "on") {
      	takeActionForSwitch(theSwitch.currentSwitch)
    }
  	subscribe(theSwitch, "switch", switcheventHandlerForTMA, [filterEvents: false])
  }
}

// called after Done is hit after selecting a Location
def initialize() {
	if (parent) { }
    else {
		log.debug "initialize"

  	if (selectedHeating) {
			addHeating()
		}
		if (selectedHotWater) {
			addHotWater()
		}
		if(selectedBulb) {
        	addBulb()
        }
        if(selectedTunableBulb) {
        	addTunableBulb()
        }
        if(selectedActivePlug) {
        	addActivePlug()
        }
		if(selectedColourBulb) {
			addColourBulb()
		}
 	 	runIn(10, 'refreshDevices') // Asynchronously refresh devices so we don't block

  	//subscribe to events for notifications if activated
  	if (preferencesSelected() == "complete") {
  		getChildDevices().each { childDevice ->
  			if (childDevice.typeName == "Hive Heating V2.0" || childDevice.typeName == "Hive Hot Water V2.0") {
  				subscribe(childDevice, "thermostatMode", modeHandler, [filterEvents: false])
    		}
    		if (childDevice.typeName == "Hive Heating V2.0") {
    			subscribe(childDevice, "temperature", tempHandler, [filterEvents: false])
                subscribe(childDevice, "maxtempthresholdbreach", evtHandler, [filterEvents: false])
    		}
  		}
  	}
  	state.maxNotificationSent = false
  	state.minNotificationSent = false
  }
}

//Event Handler for Connect App
def evtHandler(evt) {
	def msg
    if (evt.name == "maxtempthresholdbreach") {
    	msg = "Auto adjusting set temperature of ${evt.displayName} as current set temperature of ${evt.value}°C is above maximum threshold."
    	if (settings.sendMaxThresholdBreach) generateNotification(msg)    
    }
}

def tempHandler(evt) {
	def msg
    log.trace "temperature: $evt.value, $evt"

    if (settings.maxtemp != null) {
    	def maxTemp = settings.maxtemp
        if (evt.doubleValue >= maxTemp) {
        	msg = "${evt.displayName} temperature reading is very hot."
            if (state.maxNotificationSent == null || state.maxNotificationSent == false) {
            	generateNotification(msg)
                //Avoid constant messages
            	state.maxNotificationSent = true
            }
        }
        else {
        	//Reset if temperature falls back to normal levels
            state.maxNotificationSent = false
        }
    }
    else if (settings.mintemp != null) {
    	def minTemp = settings.mintemp
        if (evt.doubleValue <= minTemp) {
        	msg = "${evt.displayName} temperature reading is very cold."
            if (state.minNotificationSent == null || state.minNotificationSent == false) {
            	generateNotification(msg)
                //Avoid constant messages
            	state.minNotificationSent = true
            }
        }
        else {
        	//Reset if temperature falls back to normal levels
        	state.minNotificationSent = false
        }
    }
}

def modeHandler(evt) {
	def msg
    	if (evt.value == "heat") {
    		msg = "${evt.displayName} is set to Manual"
        	if (settings.sendSchedule) generateNotification(msg)
    	}
		else if (evt.value == "off") {
    		msg = "${evt.displayName} is turned Off"
        	if (settings.sendOff) generateNotification(msg)
    	}
    	else if (evt.value == "auto") {
    		msg = "${evt.displayName} is set to Schedule"
        	if (settings.sendManual) generateNotification(msg)
    	}
    	else if (evt.value == "emergency heat") {
    		msg = "${evt.displayName} is in Boost mode"
       	 	if (settings.sendBoost) generateNotification(msg)
    	}

}

//Event Handlers for Thermostat Mode Automation

def modeeventHandlerForTMA(evt) {
    if(allOk) {
    	log.debug "evt.value: $evt.value"
    	takeActionForMode(evt.value)
    }
}

//Handler and action for switch detection
def switcheventHandlerForTMA(evt) {
	if(allOk) {
		log.debug "evt.value: $evt.value"
    	log.debug "state.internalSwitchEvent: $state.internalSwitchEvent"
    	if (state.internalSwitchEvent == false) {
    		takeActionForSwitch(evt.value)
    	}
    	state.internalSwitchEvent = false
    }
}

def thermostateventHandlerForTMA(evt) {
	log.debug "evt.name: $evt.value"
    log.debug "state.thermostatAltered: $state.thermostatAltered"
    log.debug "alteredThermostatMode: $alteredThermostatMode"
    log.debug "state.boostingReset: $state.boostingReset"
    //If boost mode is selected as the trigger, turn switch off if boost mode finishes...
 	if (state.internalThermostatEvent == false) {
    	if (modeTrigger == "false") {
    		//if the switch is currently on, check the new mode of the thermostat and set switch to off if necessary
        	if (alteredThermostatMode == "Boost") {
            	state.internalSwitchEvent = true
        		if (evt.value != "emergency heat") {
                	//Switching the switch to off should trigger an event that resets app state
        			theSwitch.off()
        		}
            	else {
            		//Switching the switch to on so it can't be boost again
            		theSwitch.on()
                }
            }
       	 }

    	//If boost mode is selected as resumed state, need to set thermostat mode as per preference
    	if (state.boostingReset) {
    		if (evt.value != "emergency heat") {
            	state.internalThermostatEvent = true
        		changeAllThermostatsModes(thermostats, thermostatModeAfterBoost, "Boost has now finished")
            	//Reset boosting reset flag
            	state.boostingReset = false
        	}
    	}
    }
    state.internalThermostatEvent = false
}

// Thermostat Auto Mode Methods
def takeActionForSwitch(switchState) {
	// Is incoming switch is on
    if (switchState == "on")
    {
    	//Check thermostat is not already altered
    	if (!state.thermostatAltered)
        {
        	//Turn selected thermostats into selected mode

            //Add detail to push message if set to Manual is specified
        	log.debug "$theSwitch.label is on, turning thermostats to $alteredThermostatMode"
            state.internalThermostatEvent = true
            changeAllThermostatsModes(thermostats, alteredThermostatMode, "$theSwitch.label has turned on")
            //Only if reset action is specified, set the thermostatAltered state.
            if (resetThermostats == "true")
            {
        		state.thermostatAltered = true
            }
        }
    }
    else {
        log.debug "$theSwitch.label is off"
        //Check if thermostats have previously been altered
        if (state.thermostatAltered)
        {
        	//Check if user wants to reset thermostats
        	if (resetThermostats == "true")
 			{
            	log.debug "Thermostats have been altered, turning back to $resumedThermostatMode"
                //Turn selected thermostats into selected mode
                state.internalThermostatEvent = true
            	changeAllThermostatsModes(thermostats, resumedThermostatMode, "$theSwitch.label has turned off")

                //Set flag if boost mode is selected as reset state so it can be set back to desired mode in 'thermostatModeAfterBoost'
                if (resumedThermostatMode == "Boost") {
                	state.boostingReset = true
                }

            }
            //Reset app state
            state.thermostatAltered = false
        }
        else
     	{
        	log.debug "Thermostats were not altered. No action taken."
        }
    }
}

def takeActionForMode(mode) {
	// Is incoming mode in the event input enumeration
    if (mode in modes)
    {
    	//Check thermostat is not already altered
    	if (!state.thermostatAltered)
        {
        	//Turn selected thermostats into selected mode

            //Add detail to push message if set to Manual is specified
        	log.debug "$mode in selected modes, turning thermostats to $alteredThermostatMode"
            state.internalThermostatEvent = true
            changeAllThermostatsModes(thermostats, alteredThermostatMode, "mode has changed to $mode")

        	//Only if reset action is specified, set the thermostatAltered state.
            if (resetThermostats == "true")
            {
        		state.thermostatAltered = true
            }
        }
    }
    else {
        log.debug "$mode is not in select modes"
        //Check if thermostats have previously been altered
        if (state.thermostatAltered)
        {
        	//Check if user wants to reset thermostats
        	if (resetThermostats == "true")
 			{
            	log.debug "Thermostats have been altered, turning back to $resumedThermostatMode"

            	//Turn each thermostat to selected mode
                state.internalThermostatEvent = true
            	changeAllThermostatsModes(thermostats, resumedThermostatMode, "mode has changed to $mode")

 				//Set flag if boost mode is selected as reset state so it can be set back to desired mode in 'thermostatModeAfterBoost'
                if (resumedThermostatMode == "Boost") {
                	state.boostingReset = true
                }

            }
            //Reset app state
            state.thermostatAltered = false
        }
        else
     	{
        	log.debug "Thermostats were not altered. No action taken."
        }
    }
}

//Helper method for thermostat mode change
private changeAllThermostatsModes(thermostats, newThermostatMode, reason) {
	//Add detail to push message if set to Manual is specified
    def thermostatModeDetail = newThermostatMode
    if (newThermostatMode == "Set to Manual") {
    	thermostatModeDetail = thermostatModeDetail + " at $temp°C"
    }
	for (thermostat in thermostats) {
    	def message = ''
        message = "SmartThings has reset $thermostat.label to $thermostatModeDetail because $reason."
        log.info message
        send(message)
        log.debug "Setting $thermostat.label to $thermostatModeDetail"
		if (newThermostatMode == "Set to Manual") {
    		thermostat.heat()
        	thermostat.setHeatingSetpoint(temp)
    	}
    	else if (newThermostatMode == "Turn Off") {
    		thermostat.off()
    	}
    	else if (newThermostatMode == "Boost") {
    		thermostat.emergencyHeat()
    	}
    	else {
    		thermostat.auto()
		}
    }
}

private send(msg) {
    if ( sendPushMessage != "No" ) {
        log.debug( "sending push message" )
        sendPush( msg )
    }

    if ( phone ) {
        log.debug( "sending text message" )
        sendSms( phone, msg )
    }

    log.debug msg
}

private getAllOk() {
	daysOk && timeOk
}

private getDaysOk() {
	def result = true
	if (days) {
		def df = new java.text.SimpleDateFormat("EEEE")
		if (location.timeZone) {
			df.setTimeZone(location.timeZone)
		}
		else {
			df.setTimeZone(TimeZone.getTimeZone("Europe/London"))
		}
		def day = df.format(new Date())
		result = days.contains(day)
	}
	log.trace "daysOk = $result"
	result
}

private getTimeOk() {
	def result = true
	if (starting && ending) {
		def currTime = now()
		def start = timeToday(starting).time
		def stop = timeToday(ending).time
		result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
	}
	log.trace "timeOk = $result"
	result
}

private hhmm(time, fmt = "h:mm a") {
	def t = timeToday(time, location.timeZone)
	def f = new java.text.SimpleDateFormat(fmt)
	f.setTimeZone(location.timeZone ?: timeZone(time))
	f.format(t)
}

def getTimeLabel(starting, ending){

	def timeLabel = "Tap to set"

    if(starting && ending){
    	timeLabel = "Between" + " " + hhmm(starting) + " "  + "and" + " " +  hhmm(ending)
    }
    else if (starting) {
		timeLabel = "Start at" + " " + hhmm(starting)
    }
    else if(ending){
    timeLabel = "End at" + hhmm(ending)
    }
	timeLabel
}

private hideOptionsSection() {
	(starting || ending || days || modes) ? false : true
}

def greyedOutSettings(){
	def result = ""
    if (starting || ending || days || falseAlarmThreshold) {
    	result = "complete"
    }
    result
}

def greyedOutTime(starting, ending){
	def result = ""
    if (starting || ending) {
    	result = "complete"
    }
    result
}

def generateNotification(msg) {
	if (settings.sendSMS != null) {
		sendSms(sendSMS, msg)
	}
	if (settings.sendPush == true) {
		sendPush(msg)
	}
}

def updateDevices() {
	if (!state.devices) {
		state.devices = [:]
	}
	def devices = devicesList()
  state.hiveHeatingDevices = [:]
  state.hiveHotWaterDevices = [:]
  state.hiveBulbDevices = [:]
  state.hiveTunableBulbDevices = [:]
  state.hiveActivePlugDevices = [:]
  state.hiveColourBulb = [:]
  
  def selectors = []
	devices.each { device ->
        selectors.add("${device.id}")
        //Heating
        if (device.type == "heating" || device.type == "trvcontrol") {
        	def suffix = device.type == "heating" ? "Hive Heating" : "Hive TRV"
            //Heating Control
            log.debug "Identified: ${device.state.name} ${suffix}"
            def value = "${device.state.name} ${suffix}"
            def key = device.id
	        selectors.add("${key}")
            state.hiveHeatingDevices["${key}"] = value

            //Update names of devices with Hive
                def childDevice = getChildDevice("${key}")
                if (childDevice) {
                    //Update name of device if different.
                    if(childDevice.name != device.state.name + " ${suffix}") {
                            childDevice.name = device.state.name + " ${suffix}"
                            log.debug "Device's name has changed."
                    }
                }
        // Water Control
        } else if (device.type == "hotwater") {
        	log.debug "Identified: ${device.state.name} Hive Hot Water"
            def value = "${device.state.name} Hive Hot Water"
            def key = device.id
            state.hiveHotWaterDevices["${key}"] = value

            //Update names of devices
            def childDevice = getChildDevice("${device.id}")
            if (childDevice) {
            	//Update name of device if different.
                    if(childDevice.name != device.state.name + " Hive Hot Water") {
                            childDevice.name = device.state.name + " Hive Hot Water"
                            log.debug "Device's name has changed."
                    }
            }
        //Dimmable Bulb
        } else if (device.type == "tuneablelight") {
			log.debug "Identified: ${device.state.name} Hive Light Tunable"
            def value = "${device.state.name} Hive Light Tunable"
                def key = device.id
                state.hiveTunableBulbDevices["${key}"] = value
                //Update names of devices
            	def childDevice = getChildDevice("${device.id}")
            	if (childDevice) {
                	//Update name of device if different.
                	if(childDevice.name != device.state.name) {
                            childDevice.name = device.state.name
                            log.debug "Device's name has changed."
                    }
            	}
        //Colour Bulb
        } else if (device.type == "colourtuneablelight") {
			log.debug "Identified: ${device.state.name} Hive Colour Bulb"
            def value = "${device.state.name} Hive Colour Bulb"
                def key = device.id
                state.hiveColourBulb["${key}"] = value
                //Update names of devices
            	def childDevice = getChildDevice("${device.id}")
            	if (childDevice) {
                	//Update name of device if different.
                	if(childDevice.name != device.state.name) {
                            childDevice.name = device.state.name
                            log.debug "Device's name has changed."
                    }
            	}
        //White Active Light Bulb
        } else if (device.type == "warmwhitelight") {
			log.debug "Identified: ${device.state.name} Hive Light Dimmable"
            def value = "${device.state.name} Hive Light Dimmable"
                def key = device.id
                state.hiveBulbDevices["${key}"] = value
                //Update names of devices
            	def childDevice = getChildDevice("${device.id}")
            	if (childDevice) {
                	//Update name of device if different.
                	if(childDevice.name != device.state.name) {
                            childDevice.name = device.state.name
                            log.debug "Device's name has changed."
                    }
            	}
        // Active Plug            
        } else if (device.type == "activeplug") {
				log.debug "Identified: ${device.state.name} Hive Plug"
            	def value = "${device.state.name} Hive Plug"
                def key = device.id
                state.hiveActivePlugDevices["${key}"] = value
                //Update names of devices
            	def childDevice = getChildDevice("${device.id}")
            	if (childDevice) {
                	//Update name of device if different.
                	if(childDevice.name != device.state.name) {
                        childDevice.name = device.state.name
                        log.debug "Device's name has changed."
                    }
            	}
          }	 
	}
  //Remove devices if does not exist on the Hive platform
  getChildDevices().findAll { !selectors.contains("${it.deviceNetworkId}") }.each {
	log.info("Deleting ${it.deviceNetworkId}")
    try {
			deleteChildDevice(it.deviceNetworkId)
    } catch (hubitat.exception.NotFoundException e) {
    	log.info("Could not find ${it.deviceNetworkId}. Assuming manually deleted.")
    } catch (hubitat.exception.ConflictException ce) {
    	log.info("Device ${it.deviceNetworkId} in use. Please manually delete.")
    }
	}
}

def addHeating() {
	updateDevices()

	selectedHeating.each { device ->

        def childDevice = getChildDevice("${device}")

        if (!childDevice) {
    		log.info("Adding Hive Heating device ${device}: ${state.hiveHeatingDevices[device]}")

        	def data = [
                name: state.hiveHeatingDevices[device],
				label: state.hiveHeatingDevices[device],
			]
            childDevice = addChildDevice("alyc100", "Hive Heating", "$device", null, data)
            childDevice.refresh()

			log.debug "Created ${state.hiveHeatingDevices[device]} with id: ${device}"
		} else {
			log.debug "found ${state.hiveHeatingDevices[device]} with id ${device} already exists"
		}

	}
}

def addHotWater() {
	updateDevices()

	selectedHotWater.each { device ->

        def childDevice = getChildDevice("${device}")

        if (!childDevice) {
    		log.info("Adding Hive Hot Water device ${device}: ${state.hiveHotWaterDevices[device]}")

        	def data = [
                name: state.hiveHotWaterDevices[device],
				label: state.hiveHotWaterDevices[device],
			]
            childDevice = addChildDevice("alyc100", "Hive Hot Water", "$device", null, data)
            childDevice.refresh()
			log.debug "Created ${state.hiveHotWaterDevices[device]} with id: ${device}"
		} else {
			log.debug "found ${state.hiveHotWaterDevices[device]} with id ${device} already exists"
		}

	}
}

def addBulb() {
	updateDevices()

	selectedBulb.each { device ->

        def childDevice = getChildDevice("${device}")

        if (!childDevice) {
    		log.debug "Adding Hive Light Dimmable device ${device}: ${state.hiveBulbDevices[device]}"

        	def data = [
                name: state.hiveBulbDevices[device],
				label: state.hiveBulbDevices[device],
			]
            
            log.debug data
            
            childDevice = addChildDevice("alyc100", "Hive Active Light", "$device", null, data)
            childDevice.refresh()
            
			log.debug "Created ${state.hiveBulbDevices[device]} with id: ${device}"
		} else {
			log.debug "found ${state.hiveBulbDevices[device]} with id ${device} already exists"
		}

	}
}

def addTunableBulb() {
	updateDevices()

	selectedTunableBulb.each { device ->

        def childDevice = getChildDevice("${device}")

        if (!childDevice) {
    		log.debug "Adding Hive Light Tuneable device ${device}: ${state.hiveTunableBulbDevices[device]}"

        	def data = [
                name: state.hiveTunableBulbDevices[device],
				label: state.hiveTunableBulbDevices[device],
			]
            
            log.debug data
            
            childDevice = addChildDevice("alyc100", "Hive Active Light Tuneable", "$device", null, data)
            childDevice.refresh()
            
			log.debug "Created ${state.hiveTunableBulbDevices[device]} with id: ${device}"
		} else {
			log.debug "found ${state.hiveTunableBulbDevices[device]} with id ${device} already exists"
		}

	}
}

def addColourBulb() {
	updateDevices()

	selectedColourBulb.each { device ->

        def childDevice = getChildDevice("${device}")

        if (!childDevice) {
    		log.debug "Adding Hive Light Colour device ${device}: ${state.hiveBulbDevices[device]}"

        	def data = [
                name: state.hiveColourBulb[device],
				label: state.hiveColourBulb[device],
			]
            
            log.debug data
            
            childDevice = addChildDevice("alyc100", "Hive Active Light Colour Tuneable", "$device", null, data)
            childDevice.refresh()
            
			log.debug "Created ${state.hiveColourBulb[device]} with id: ${device}"
            
		} else {
			log.debug "found ${state.hiveColourBulb[device]} with id ${device} already exists"
		}

	}
}

def addActivePlug() {
	updateDevices()

	selectedActivePlug.each { device ->

        def childDevice = getChildDevice("${device}")

        if (!childDevice) {
    		log.debug "Adding Hive Plug device ${device}: ${state.hiveActivePlugDevices[device]}"

        	def data = [
                name: state.hiveActivePlugDevices[device],
				label: state.hiveActivePlugDevices[device],
			]
            
            log.debug data
            
            childDevice = addChildDevice("alyc100", "Hive Active Plug", "$device", null, data)
            childDevice.refresh()
            
			log.debug "Created ${state.hiveActivePlugDevices[device]} with id: ${device}"
		} else {
			log.debug "found ${state.hiveActivePlugDevices[device]} with id ${device} already exists"
		}

	}
}

def refreshDevices() {
	log.info("Refreshing all devices...")
	getChildDevices().each { device ->
		device.refresh()
	}
}

def devicesList() {
	logErrors([]) {
		def resp = apiGET("/products")
		if (resp.status == 200) {
			return resp.data
		} else {
			log.error("Non-200 from device list call. ${resp.status} ${resp.data}")
			return []
		}
	}
}

def getDeviceStatus(id) {
	def retVal = []
	def resp = apiGET("/products")
	if (resp.status == 200) {
		resp.data.eachWithIndex { currentDevice, i ->
        	if(currentDevice.id == id || (currentDevice.type + "/" + currentDevice.id) == id) { 
                retVal = resp.data[i]
            }
        }
                
	} else {
		log.error("Non-200 from device list call. ${resp.status} ${resp.data}")
	}
    return retVal
}

def apiGET(path, body = [:]) {
	try {
    	if(!isLoggedIn()) {
			log.debug "Need to login"
			getBeekeeperAccessToken()
		}
        log.debug("Beginning API GET: ${apiBeekeeperUKURL(path)}, ${apiRequestHeaders()}")

        httpGet(uri: apiBeekeeperUKURL(path), contentType: 'application/json', headers: apiRequestHeaders()) {response ->
			logResponse(response)
			return response
		}
	} catch (groovyx.net.http.HttpResponseException e) {
		logResponse(e.response)
		return e.response
	}
}

def apiPOST(path, body = [:]) {
	try {
    	if(!isLoggedIn()) {
			log.debug "Need to login"
			getBeekeeperAccessToken()
		}
		log.debug("Beginning API POST: ${path}, ${body}")

		httpPostJson(uri: apiBeekeeperUKURL(path), body: body, headers: apiRequestHeaders() ) {response ->
			logResponse(response)
			return response
		}
	} catch (groovyx.net.http.HttpResponseException e) {
		logResponse(e.response)
		return e.response
	}
}

def getBeekeeperAccessToken() {
	try {
    	def tokens
        if (state.beekeeperToken) {
        	tokens = [	"token": state.beekeeperToken,
            			"refreshToken": state.beekeeperRefreshToken,
                        "accessToken": state.beekeeperAccessToken ]
        } else {
        	tokens = initTokens()
        }
        def params = [
			uri: apiBeekeeperURL('/cognito/refresh-token'),
        	contentType: 'application/json',
            body: tokens
        ]

		state.cookie = ''

		httpPostJson(params) {response ->
			log.debug "Request was successful, $response.status"
			log.debug response.headers

        	state.cookie = response?.headers?.'Set-Cookie'?.split(";")?.getAt(0)
			log.debug "Adding cookie to collection: $cookie"
        	log.debug "auth: $response.data"
			log.debug "cookie: $state.cookie"
        	log.debug "token: ${response.data.token}"
        	log.debug "refreshToken: ${response.data.refreshToken}"
        	log.debug "accessToken: ${response.data.accessToken}"
            

        	state.beekeeperToken = response.data.token
            state.beekeeperRefreshToken = response.data.refreshToken
            state.beekeeperAccessToken = response.data.accessToken
        	// set the expiration to 20 minutes
			state.beekeeperToken_expires_at = new Date().getTime() + 1200000
            state.loginerrors = null
		}
        
    } catch (groovyx.net.http.HttpResponseException e) {
    	if (e.response.status == 401) {
			state.remove("beekeeperToken")
			state.remove("beekeeperRefreshToken")
			state.remove("beekeeperAccessToken")
			state.remove("beekeeperToken_expires_at")
        }
   		state.loginerrors = "Error: ${e.response.status}: ${e.response.data}"
    	logResponse(e.response)
		return e.response
    }
}

def apiRequestHeaders() {
	return [
        'Authorization': "Bearer ${state.beekeeperToken}"
    ]
}

def isLoggedIn() {
	state.remove("hiveAccessToken")
	log.debug "Calling isLoggedIn()"
	log.debug "isLoggedIn state $state.beekeeperToken"
	if(!state.beekeeperToken) {
		log.debug "No state.beekeeperToken"
		return false
	}

	def now = new Date().getTime()
    return state.beekeeperToken_expires_at > now
}


def isTmaAppInst() {
	def chldCnt = 0
	childApps?.each { cApp ->
//        if(cApp?.name != getWatchdogAppChildName()) { chldCnt = chldCnt + 1 }
		chldCnt = chldCnt + 1
	}
	return (chldCnt > 0) ? true : false
}

def logResponse(response) {
	log.info("Status: ${response.status}")
	log.info("Body: ${response.data}")
}

def logErrors(options = [errorReturn: null, logObject: log], Closure c) {
	try {
		return c()
	} catch (groovyx.net.http.HttpResponseException e) {
		log.error("got error: ${e}, body: ${e.getResponse().getData()}")
		if (e.statusCode == 401) { // token is expired
			state.remove("beekeeperToken")
			state.remove("beekeeperRefreshToken")
			state.remove("beekeeperAccessToken")
			state.remove("beekeeperToken_expires_at")
			log.warn "Access token is not valid"
		}
		return options.errorReturn
	} catch (java.net.SocketTimeoutException e) {
		log.warn "Connection timed out, not much we can do here"
		return options.errorReturn
	}
}

def appName() 		{ return "${parent ? "Hive Mode Automation" : "Hive (Connect)"}" }

Bingo! - No idea what happened there, but all working now :+1:

Thanks :smiley:

1 Like

Happy days :grin:

Now I just need to get the Hive TRV's working with 'Schedule' and 'Off'! :laughing:

Interesting, have you had trouble getting them working with the code from alyc100?

Simple fix - it just needs the Driver (DTH) manually changing as the App (SmartApp) imports them with the Heating Driver by default:

Awesome! Completely missed that! Have you completely offloaded ST yet?

That explains why I couldn't get the sodding thing working with webcore in ST :slight_smile:

I've got rid of the ST hub now (that felt kinda good!) - now just using the ST Cloud for my Neato integration and my Switchbot Curtain!

Same here, this thing is life changing! I'm doing a lot less swearing as I stand in the middle of a room waiting for the lights!

Or frantically checking Facebook and the ST Forums to check if it's down for just me or everyone else.... :roll_eyes:

Yep. Pending removal of the IDE did it for me:

TRV imported, just need to comment out these two:

    capability "tigerdrum36561.boostLabel"
    capability "tigerdrum36561.boostLength"

more testing!

Can't seem to change the mode of the TRV with HE, is that what you are expecting?

And moving the TRV from Off to Automode generates this:

[app:426](http://192.168.15.102/logs#app426)2020-12-09 08:32:41.141 [error](http://192.168.15.102/installedapp/configure/426)groovy.lang.MissingMethodException: No signature of method: user_app_alyc100_Hive__Connect__132.getDeviceInfo() is applicable for argument types: (null) values: [null] on line 300 (getDeviceInfo)

[app:426](http://192.168.15.102/logs#app426)2020-12-09 08:32:41.113 [error](http://192.168.15.102/installedapp/configure/426)groovy.lang.MissingMethodException: No signature of method: user_app_alyc100_Hive__Connect__132.getDeviceID() is applicable for argument types: (java.lang.String) values: [7f8d931e-4e30-42c0-9150-67c5091dc180] on line 282 (getDeviceID)

[app:426](http://192.168.15.102/logs#app426)2020-12-09 08:32:41.083 [error](http://192.168.15.102/installedapp/configure/426)groovy.lang.MissingMethodException: No signature of method: user_app_alyc100_Hive__Connect__132.getDeviceTRVStatus() is applicable for argument types: (java.lang.String) values: [7f8d931e-4e30-42c0-9150-67c5091dc180] Possible solutions: getDeviceStatus(java.lang.Object) on line 288 (getDeviceTRVStatus)

[app:426](http://192.168.15.102/logs#app426)2020-12-09 08:32:27.778 [info](http://192.168.15.102/installedapp/configure/426)Body: [mode:SCHEDULE]

[app:426](http://192.168.15.102/logs#app426)2020-12-09 08:32:27.768 [info](http://192.168.15.102/installedapp/configure/426)Status: 200

[app:426](http://192.168.15.102/logs#app426)2020-12-09 08:32:26.008 [debug](http://192.168.15.102/installedapp/configure/426)Beginning API POST: /nodes/trvcontrol/e8f2c4a5-be3b-4908-b9f2-fc184aed758d, [mode:SCHEDULE]

[app:426](http://192.168.15.102/logs#app426)2020-12-09 08:32:26.004 [debug](http://192.168.15.102/installedapp/configure/426)isLoggedIn state

However, if the TRV is Off, and you change it to Auto using HE it works! the Hive apps reflects the mode change. The HE side of things never updates though?

@guy.mayhew @Baz2473 I don't suppose either of you have any idea on the TRV question do you? I have a very specific use case and it would be awesome to be able to change the mode of the TRV (which works) and have the status reflected in HE (which doesn't).

Thanks. P