def start(self):
     self.access_token = self.controller.polyConfig['customData']['access_token']
     self.NuHeat = NuHeat(self.access_token)
     energy_used = self.NuHeat.get_energy_log_year(self.primary, self.year)
     if energy_used is not None:
         self.setDriver('GV0', energy_used[0], uom=45)
         self.setDriver('ST', energy_used[1], uom=33)
         self.setDriver('GV1', energy_used[2], uom=103)
     else:
         polyinterface.LOGGER.error("Energy Log Year Returned: None")
Пример #2
0
    def test_successful_authentication(self):
        response_data = load_fixture("auth_success.json")
        responses.add(responses.POST,
                      config.AUTH_URL,
                      status=200,
                      body=json.dumps(response_data),
                      content_type="application/json")

        api = NuHeat("*****@*****.**", "secure-password")
        self.assertIsNone(api._session_id)
        api.authenticate()
        self.assertEqual(api._session_id, response_data.get("SessionId"))
Пример #3
0
    def test_authentication_error(self):
        response_data = load_fixture("auth_error.json")
        responses.add(responses.POST,
                      config.AUTH_URL,
                      status=200,
                      body=json.dumps(response_data),
                      content_type="application/json")

        api = NuHeat("*****@*****.**", "secure-password")
        with self.assertRaises(Exception) as _:
            api.authenticate()
            self.assertIsNone(api._session_id)
class EnergyLogYearNode(polyinterface.Node):
    def __init__(self, controller, primary, address, name):
        super(EnergyLogYearNode, self).__init__(controller, primary, address, name)
        self.access_token = None
        self.NuHeat = None
        self.tz = controller.polyConfig['customParams']['tz']
        self.date = datetime.now(pytz.timezone(self.tz)).strftime('%Y-%m-%d')
        self.year = str(date.today().year)

    def start(self):
        self.access_token = self.controller.polyConfig['customData']['access_token']
        self.NuHeat = NuHeat(self.access_token)
        energy_used = self.NuHeat.get_energy_log_year(self.primary, self.year)
        if energy_used is not None:
            self.setDriver('GV0', energy_used[0], uom=45)
            self.setDriver('ST', energy_used[1], uom=33)
            self.setDriver('GV1', energy_used[2], uom=103)
        else:
            polyinterface.LOGGER.error("Energy Log Year Returned: None")

    def query(self, command=None):
        self.reportDrivers()

    # "Hints See: https://github.com/UniversalDevicesInc/hints"
    # hint = [1,2,3,4]
    drivers = [
        # {'driver': 'ST', 'value': 0, 'uom': 2},
        {'driver': 'GV0', 'value': 0, 'uom': 45},
        {'driver': 'ST', 'value': 0, 'uom': 33},
        {'driver': 'GV1', 'value': 0, 'uom': 104}
    ]

    id = 'ENERGYLOG'

    commands = {'QUERY': query}
 def test_repr_without_data(self, _):
     api = NuHeat(None, None)
     serial_number = "serial-123"
     thermostat = NuHeatThermostat(api, serial_number)
     self.assertEqual(
         str(thermostat),
         "<NuHeatThermostat id='{}' temperature='{}F / {}C' target='{}F / {}C'>"
         .format(serial_number, None, None, None, None))
Пример #6
0
    def refresh_token(self):
        token_url = "https://identity.mynuheat.com/connect/token"

        if 'refresh_token' in self.polyConfig['customData']:
            refresh_token = self.polyConfig['customData']['refresh_token']

            _response_type = "token"
            _scope = "openapi profile openid offline_access"

            headers = {'Content-Type': 'application/x-www-form-urlencoded'}

            payload = {"grant_type": "refresh_token",
                       "client_id": self.server_data['clientId'],
                       "response_type": _response_type,
                       "client_secret": self.server_data['clientSecret'],
                       "scope": _scope,
                       "redirect_uri": self.server_data['url'],
                       "refresh_token": refresh_token}

            try:
                r = requests.post(token_url, headers=headers, data=payload)
                if r.status_code == requests.codes.ok:
                    try:
                        resp = r.json()
                        id_token = resp['id_token']
                        access_token = resp['access_token']
                        refresh_token = resp['refresh_token']
                        expires_in = resp['expires_in']

                        cust_data = {'id_token': id_token,
                                     'access_token': access_token,
                                     'refresh_token': refresh_token,
                                     'expires_in': expires_in}

                        self.saveCustomData(cust_data)
                        self.NuHeat = NuHeat(access_token)
                        return True
                    except KeyError as ex:
                        LOGGER.error("get_token Error: " + str(ex))
                else:
                    return False
            except requests.exceptions.RequestException as e:
                LOGGER.error("NuHeat.set_thermostat_setpoint Error: " + str(e))
        else:
            return False
Пример #7
0
    def test_get_request(self):
        url = "http://www.example.com/api"
        params = dict(test="param")
        responses.add(responses.GET,
                      url,
                      status=200,
                      content_type="application/json")
        api = NuHeat(None, None)
        response = api.request(url, method="GET", params=params)

        self.assertEqual(response.status_code, 200)
        self.assertUrlsEqual(response.request.url,
                             "{}?{}".format(url, urlencode(params)))
        request_headers = response.request.headers
        self.assertEqual(request_headers["Origin"],
                         config.REQUEST_HEADERS["Origin"])
        self.assertEqual(request_headers["Content-Type"],
                         config.REQUEST_HEADERS["Content-Type"])
    def start(self):
        self.access_token = self.controller.polyConfig['customData'][
            'access_token']
        self.NuHeat = NuHeat(self.access_token)
        thermostats = self.NuHeat.get_thermostat()
        if thermostats is not None:
            for stat in thermostats:
                if stat['serialNumber'] == self.address:
                    if self.temp_uom == 17:
                        clitemp = self.NuHeat.nuheat_celsius_to_fahrenheit(
                            stat['currentTemperature'])
                        clisph = self.NuHeat.nuheat_celsius_to_fahrenheit(
                            stat['setPointTemp'])
                    else:
                        clitemp = self.NuHeat.nuheat_celsius_to_normal(
                            stat['currentTemperature'])
                        clisph = self.NuHeat.nuheat_celsius_to_normal(
                            stat['setPointTemp'])

                    climd = 0
                    if stat['operatingMode'] == 1:
                        climd = 3
                    elif stat['operatingMode'] == 2:
                        climd = 1

                    if stat['isHeating']:
                        clihcs = 1
                    else:
                        clihcs = 0

                    # self.setDriver('ST', clitemp, uom=self.temp_uom)
                    # self.setDriver('CLISPH', clisph, uom=self.temp_uom)
                    # self.setDriver('CLIMD', climd, uom=67)
                    # self.setDriver('CLIHCS', clihcs, uom=66)
                    self.setDriver('ST', clitemp)
                    self.setDriver('CLISPH', clisph)
                    self.setDriver('CLIMD', climd)
                    self.setDriver('CLIHCS', clihcs)

                else:
                    LOGGER.error("Thermostat Serial Number not available")
        else:
            LOGGER.error(
                "thermostat_node.Nuheat.get_thermostat: Returned None")
    def test_get_data_401(self):
        # First request (when initializing the thermostat) is successful
        response_data = load_fixture("thermostat.json")
        responses.add(responses.GET,
                      config.THERMOSTAT_URL,
                      status=200,
                      body=json.dumps(response_data),
                      content_type="application/json")

        # A later, second request throws 401 Unauthorized
        responses.add(responses.GET, config.THERMOSTAT_URL, status=401)

        # Attempt to reauthenticate
        auth_data = load_fixture("auth_success.json")
        responses.add(responses.POST,
                      config.AUTH_URL,
                      status=200,
                      body=json.dumps(auth_data),
                      content_type="application/json")

        # Third request is successful
        responses.add(responses.GET,
                      config.THERMOSTAT_URL,
                      status=200,
                      body=json.dumps(response_data),
                      content_type="application/json")

        bad_session_id = "my-bad-session"
        good_session_id = auth_data.get("SessionId")
        api = NuHeat(None, None, session_id=bad_session_id)
        serial_number = response_data.get("SerialNumber")

        thermostat = NuHeatThermostat(api, serial_number)
        thermostat.get_data()
        self.assertTrue(isinstance(thermostat, NuHeatThermostat))

        api_calls = responses.calls
        self.assertEqual(len(api_calls), 4)

        unauthorized_attempt = api_calls[1]
        params = {"sessionid": bad_session_id, "serialnumber": serial_number}
        request_url = "{}?{}".format(config.THERMOSTAT_URL, urlencode(params))
        self.assertEqual(unauthorized_attempt.request.method, "GET")
        self.assertUrlsEqual(unauthorized_attempt.request.url, request_url)
        self.assertEqual(unauthorized_attempt.response.status_code, 401)

        auth_call = api_calls[2]
        self.assertEqual(auth_call.request.method, "POST")
        self.assertUrlsEqual(auth_call.request.url, config.AUTH_URL)

        second_attempt = api_calls[3]
        params["sessionid"] = good_session_id
        request_url = "{}?{}".format(config.THERMOSTAT_URL, urlencode(params))
        self.assertEqual(second_attempt.request.method, "GET")
        self.assertUrlsEqual(second_attempt.request.url, request_url)
        self.assertEqual(second_attempt.response.status_code, 200)
 def test_repr_with_data(self, _):
     api = NuHeat(None, None)
     serial_number = "serial-123"
     thermostat = NuHeatThermostat(api, serial_number)
     thermostat.temperature = 2000
     thermostat.target_temperature = 5000
     self.assertEqual(
         str(thermostat),
         "<NuHeatThermostat id='{}' temperature='{}F / {}C' target='{}F / {}C'>"
         .format(serial_number, 68, 20, 122, 50))
    def test_set_data(self, _):
        responses.add(responses.POST,
                      config.THERMOSTAT_URL,
                      status=200,
                      content_type="application/json")

        api = NuHeat(None, None, session_id="my-session")
        serial_number = "my-thermostat"
        params = {"sessionid": api._session_id, "serialnumber": serial_number}
        request_url = "{}?{}".format(config.THERMOSTAT_URL, urlencode(params))
        post_data = {"test": "data"}
        thermostat = NuHeatThermostat(api, serial_number)
        thermostat.set_data(post_data)

        api_call = responses.calls[0]
        self.assertEqual(api_call.request.method, "POST")
        self.assertUrlsEqual(api_call.request.url, request_url)
        self.assertEqual(api_call.request.body, urlencode(post_data))
    def test_get_data(self):
        response_data = load_fixture("thermostat.json")
        responses.add(responses.GET,
                      config.THERMOSTAT_URL,
                      status=200,
                      body=json.dumps(response_data),
                      content_type="application/json")
        api = NuHeat(None, None, session_id="my-session")
        serial_number = response_data.get("SerialNumber")
        params = {"sessionid": api._session_id, "serialnumber": serial_number}
        request_url = "{}?{}".format(config.THERMOSTAT_URL, urlencode(params))

        thermostat = NuHeatThermostat(api, serial_number)
        thermostat.get_data()

        api_calls = responses.calls

        # Data is fetched once on instantiation and once on get_data()
        self.assertEqual(len(api_calls), 2)

        api_call = api_calls[0]
        self.assertEqual(api_call.request.method, "GET")
        self.assertUrlsEqual(api_call.request.url, request_url)

        self.assertEqual(thermostat._data, response_data)
        self.assertEqual(thermostat.heating, response_data["Heating"])
        self.assertEqual(thermostat.online, response_data["Online"])
        self.assertEqual(thermostat.room, response_data["Room"])
        self.assertEqual(thermostat.serial_number,
                         response_data["SerialNumber"])
        self.assertEqual(thermostat.temperature, response_data["Temperature"])
        self.assertEqual(thermostat.min_temperature, response_data["MinTemp"])
        self.assertEqual(thermostat.max_temperature, response_data["MaxTemp"])
        self.assertEqual(thermostat.target_temperature,
                         response_data["SetPointTemp"])
        self.assertEqual(thermostat.schedule_mode,
                         response_data["ScheduleMode"])
Пример #13
0
 def test_init_with_session(self):
     existing_session_id = "passed-session"
     api = NuHeat("*****@*****.**", "secure-password",
                  existing_session_id)
     self.assertEqual(api._session_id, existing_session_id)
     api.authenticate()
 def test_init(self, _):
     api = NuHeat(None, None)
     serial_number = "serial-123"
     thermostat = NuHeatThermostat(api, serial_number)
     self.assertEqual(thermostat.serial_number, serial_number)
     self.assertEqual(thermostat._session, api)
Пример #15
0
from nuheat import NuHeat

# Initalize an API session with your login credentials
api = NuHeat("https://www.mythermostat.info/api", "<email>", "<pwd>")
api.authenticate()

thermostat = api.get_thermostat("<serial>")

current = thermostat.celsius
target = thermostat.target_celsius

heating = thermostat.heating
online = thermostat.online
serial = thermostat.serial_number
Пример #16
0
 def test_repr(self):
     email = "*****@*****.**"
     api = NuHeat(email, "secure-password")
     self.assertEqual(str(api), "<NuHeat username='******'>".format(email))
Пример #17
0
from nuheat import NuHeat

# Initalize an API session with your login credentials
api = NuHeat("AU", "email", "pwd")
api.authenticate()

thermostat = api.get_thermostat("device_id")

current = thermostat.celsius
target = thermostat.target_celsius

heating = thermostat.heating
online = thermostat.online
serial = thermostat.serial_number
Пример #18
0
 def test_get_thermostat(self, _):
     api = NuHeat(None, None)
     serial_number = "serial-123"
     thermostat = api.get_thermostat(serial_number)
     self.assertTrue(isinstance(thermostat, NuHeatThermostat))
Пример #19
0
class Controller(polyinterface.Controller):
    def __init__(self, polyglot):
        super(Controller, self).__init__(polyglot)
        self.name = 'NuHeat'
        # self.poly.onConfig(self.process_config)
        self.server_data = {}
        self.temperature_scale = None
        self.temp_uom = None
        self.NuHeat = None
        self.tz = None
        self.disco = 0

    def start(self):
        if self.get_credentials():
            self.check_params()
            if self.refresh_token():
                self.discover()
            else:
                self.auth_prompt()

    def get_credentials(self):
        LOGGER.info('---- Environment: ' + self.poly.stage + ' ----')
        if self.poly.stage == 'test':
            if 'clientId' in self.poly.init['oauth']['test']:
                self.server_data['clientId'] = self.poly.init['oauth']['test']['clientId']
            else:
                LOGGER.error('Unable to find Client ID in the init data')
                return False
            if 'secret' in self.poly.init['oauth']['test']:
                self.server_data['clientSecret'] = self.poly.init['oauth']['test']['secret']
            else:
                LOGGER.error('Unable to find Client Secret in the init data')
                return False
            if 'redirectUrl' in self.poly.init['oauth']['test']:
                self.server_data['url'] = self.poly.init['oauth']['test']['redirectUrl']
            else:
                LOGGER.error('Unable to find URL in the init data')
                return False
            if self.poly.init['worker']:
                self.server_data['worker'] = self.poly.init['worker']
            else:
                return False
            return True
        elif self.poly.stage == 'prod':
            if 'clientId' in self.poly.init['oauth']['prod']:
                self.server_data['clientId'] = self.poly.init['oauth']['prod']['clientId']
            else:
                LOGGER.error('Unable to find Client ID in the init data')
                return False
            if 'secret' in self.poly.init['oauth']['test']:
                self.server_data['clientSecret'] = self.poly.init['oauth']['prod']['secret']
            else:
                LOGGER.error('Unable to find Client Secret in the init data')
                return False
            if 'redirectUrl' in self.poly.init['oauth']['test']:
                self.server_data['url'] = self.poly.init['oauth']['prod']['redirectUrl']
            else:
                LOGGER.error('Unable to find URL in the init data')
                return False
            if self.poly.init['worker']:
                self.server_data['worker'] = self.poly.init['worker']
            else:
                return False
            return True

    def auth_prompt(self):
        _auth_url = "https://identity.mynuheat.com/connect/authorize"
        _response_type = "code"
        _scope = "openapi openid profile offline_access"

        _user_auth_url = _auth_url + \
                        "?client_id=" + self.server_data['clientId'] + \
                        "&response_type=" + _response_type + \
                        "&scope=" + _scope + \
                        "&redirect_uri=" + self.server_data['url'] + \
                        "&state=" + self.server_data['worker']

        self.addNotice(
            {'myNotice': 'Click <a href="' + _user_auth_url + '">here</a> to link your NuHeat account'})

    def oauth(self, oauth):
        LOGGER.info('OAUTH Received: {}'.format(oauth))
        if 'code' in oauth:
            if self.get_token(oauth['code']):
                self.remove_notices_all()
                self.discover()

    def get_token(self, code):
        _token_url = "https://identity.mynuheat.com/connect/token"
        _response_type = "token"
        _scope = "openapi openid profile offline_access"

        headers = {'Content-Type': 'application/x-www-form-urlencoded'}

        payload = {"grant_type": "authorization_code",
                   "code": code,
                   "client_id": self.server_data['clientId'],
                   "response_type": _response_type,
                   "client_secret": self.server_data['clientSecret'],
                   "scope": _scope,
                   "redirect_uri": self.server_data['url']}

        try:
            r = requests.post(_token_url, headers=headers, data=payload)
            if r.status_code == requests.codes.ok:
                try:
                    resp = r.json()
                    id_token = resp['id_token']
                    access_token = resp['access_token']
                    refresh_token = resp['refresh_token']
                    expires_in = resp['expires_in']

                    cust_data = {'id_token': id_token,
                                 'access_token': access_token,
                                 'refresh_token': refresh_token,
                                 'expires_in': expires_in}

                    self.saveCustomData(cust_data)
                    self.NuHeat = NuHeat(access_token)
                    self.remove_notices_all()
                    return True
                except KeyError as ex:
                    LOGGER.error("get_token Error: " + str(ex))
            else:
                return False
        except requests.exceptions.RequestException as e:
            LOGGER.error("NuHeat.set_thermostat_setpoint Error: " + str(e))

    def refresh_token(self):
        token_url = "https://identity.mynuheat.com/connect/token"

        if 'refresh_token' in self.polyConfig['customData']:
            refresh_token = self.polyConfig['customData']['refresh_token']

            _response_type = "token"
            _scope = "openapi profile openid offline_access"

            headers = {'Content-Type': 'application/x-www-form-urlencoded'}

            payload = {"grant_type": "refresh_token",
                       "client_id": self.server_data['clientId'],
                       "response_type": _response_type,
                       "client_secret": self.server_data['clientSecret'],
                       "scope": _scope,
                       "redirect_uri": self.server_data['url'],
                       "refresh_token": refresh_token}

            try:
                r = requests.post(token_url, headers=headers, data=payload)
                if r.status_code == requests.codes.ok:
                    try:
                        resp = r.json()
                        id_token = resp['id_token']
                        access_token = resp['access_token']
                        refresh_token = resp['refresh_token']
                        expires_in = resp['expires_in']

                        cust_data = {'id_token': id_token,
                                     'access_token': access_token,
                                     'refresh_token': refresh_token,
                                     'expires_in': expires_in}

                        self.saveCustomData(cust_data)
                        self.NuHeat = NuHeat(access_token)
                        return True
                    except KeyError as ex:
                        LOGGER.error("get_token Error: " + str(ex))
                else:
                    return False
            except requests.exceptions.RequestException as e:
                LOGGER.error("NuHeat.set_thermostat_setpoint Error: " + str(e))
        else:
            return False

    def shortPoll(self):
        if self.disco == 1:
            for node in self.nodes:
                if self.nodes[node].address != self.address:
                    self.nodes[node].start()

    def longPoll(self):
        """
        The token expires every 1 hour (3600 seconds).  The long Poll is set to 15 minutes by default
        and refreshes on Nodeserver start.
        :return:
        """
        self.refresh_token()

    def query(self, command=None):
        self.check_params()
        for node in self.nodes:
            self.nodes[node].reportDrivers()

    def discover(self, *args, **kwargs):
        account_info = self.NuHeat.get_account()
        if account_info is not None:
            self.temperature_scale = account_info['temperatureScale']

            if self.temperature_scale == "Fahrenheit":
                self.temp_uom = 17
            else:
                self.temp_uom = 4

        thermostats = self.NuHeat.get_thermostat()
        for stat in thermostats:
            name = stat['name']
            stat_address = stat['serialNumber']
            energy_log_day_address = "eld" + str(stat_address)
            energy_log_week_address = "elw" + str(stat_address)
            energy_log_year_address = "ely" + str(stat_address)

            if self.temp_uom == 17:
                self.addNode(ThermostatNode_F(self, stat_address, stat_address, name))
            elif self.temp_uom == 4:
                self.addNode(ThermostatNode_C(self, stat_address, stat_address, name))
            else:
                LOGGER.error("Invalid Temperature Measure (UOM) Not Fahrenheit or Celsius")
            time.sleep(2)

            self.addNode(EnergyLogDayNode(self, stat_address, energy_log_day_address, "Energy-Day"))
            time.sleep(2)

            self.addNode(EnergyLogWeekNode(self, stat_address, energy_log_week_address, "Energy-Week"))
            time.sleep(2)

            self.addNode(EnergyLogYearNode(self, stat_address, energy_log_year_address, "Energy-Year"))
            time.sleep(2)

        self.disco = 1

    def delete(self):
        LOGGER.info('Removing Nuheat Nodeserver')

    def stop(self):
        LOGGER.debug('NodeServer stopped.')

    def process_config(self, config):
        # this seems to get called twice for every change, why?
        # What does config represent?
        LOGGER.info("process_config: Enter config={}".format(config));
        LOGGER.info("process_config: Exit");

    def check_params(self):
        self.removeNoticesAll()
        default_tz = "America/New_York"

        if 'tz' in self.polyConfig['customParams']:
            self.tz = self.polyConfig['customParams']['tz']
        else:
            self.tz = default_tz
            LOGGER.info("Change the TimeZone to match your location and restart")
            self.addNotice("Change the TimeZone to match your location and restart")

        self.addCustomParam({'tz': default_tz})

    def remove_notice_test(self, command):
        LOGGER.info('remove_notice_test: notices={}'.format(self.poly.config['notices']))
        # Remove all existing notices
        self.removeNotice('test')

    def remove_notices_all(self):
        LOGGER.info('remove_notices_all: notices={}'.format(self.poly.config['notices']))
        # Remove all existing notices
        self.removeNoticesAll()

    def update_profile(self, command):
        LOGGER.info('update_profile:')
        st = self.poly.installprofile()
        return st

    id = 'controller'
    commands = {
        'QUERY': query,
        'DISCOVER': discover,
        'UPDATE_PROFILE': update_profile
    }
    drivers = [{'driver': 'ST', 'value': 1, 'uom': 2}]
Пример #20
0
class ThermostatNode_F(polyinterface.Node):
    def __init__(self, controller, primary, address, name):
        super(ThermostatNode_F, self).__init__(controller, primary, address,
                                               name)
        self.access_token = None
        self.NuHeat = None
        self.temp_uom = controller.temp_uom

    def start(self):
        self.access_token = self.controller.polyConfig['customData'][
            'access_token']
        self.NuHeat = NuHeat(self.access_token)
        thermostats = self.NuHeat.get_thermostat()
        if thermostats is not None:
            for stat in thermostats:
                if stat['serialNumber'] == self.address:
                    if self.temp_uom == 17:
                        clitemp = self.NuHeat.nuheat_celsius_to_fahrenheit(
                            stat['currentTemperature'])
                        clisph = self.NuHeat.nuheat_celsius_to_fahrenheit(
                            stat['setPointTemp'])
                    else:
                        clitemp = self.NuHeat.nuheat_celsius_to_normal(
                            stat['currentTemperature'])
                        clisph = self.NuHeat.nuheat_celsius_to_normal(
                            stat['setPointTemp'])

                    climd = 0
                    if stat['operatingMode'] == 1:
                        climd = 3
                    elif stat['operatingMode'] == 2:
                        climd = 1

                    if stat['isHeating']:
                        clihcs = 1
                    else:
                        clihcs = 0

                    # self.setDriver('ST', clitemp, uom=self.temp_uom)
                    # self.setDriver('CLISPH', clisph, uom=self.temp_uom)
                    # self.setDriver('CLIMD', climd, uom=67)
                    # self.setDriver('CLIHCS', clihcs, uom=66)
                    self.setDriver('ST', clitemp)
                    self.setDriver('CLISPH', clisph)
                    self.setDriver('CLIMD', climd)
                    self.setDriver('CLIHCS', clihcs)

                else:
                    LOGGER.error("Thermostat Serial Number not available")
        else:
            LOGGER.error(
                "thermostat_node.Nuheat.get_thermostat: Returned None")

    def query(self, command=None):
        self.reportDrivers()

    def setpoint_heat(self, command):
        val = command['value']

        if self.temp_uom == 17:
            new_setpoint = self.NuHeat.nuheat_fahrenheit_to_celsius_json(val)
        else:
            new_setpoint = self.NuHeat.nuheat_celsius_to_json(val)

        _status = self.NuHeat.set_thermostat_setpoint(self.address,
                                                      new_setpoint)
        if _status is not None:
            self.setDriver('CLISPH', val)
        else:
            print("thermostat_node.setpoint_heat: " + str(_status))

    # "Hints See: https://github.com/UniversalDevicesInc/hints"
    # hint = [1, 12, 1, 0]
    drivers = [{
        'driver': 'ST',
        'value': 0,
        'uom': 17
    }, {
        'driver': 'CLISPH',
        'value': 0,
        'uom': 17
    }, {
        'driver': 'CLIMD',
        'value': 0,
        'uom': 67
    }, {
        'driver': 'CLIHCS',
        'value': 0,
        'uom': 66
    }]

    id = 'THERMOSTAT_F'

    commands = {'QUERY': query, 'CLISPH': setpoint_heat}