Integration to Delta VoiceIQ

Try this instead:

def response
httpPost(uri: apiUrl, headers: headers)
{
   resp ->
     response = resp
}

... and then see if the rest of your code makes it any farther.

1 Like

Unfortunately…

I wonder if I implemented the change correctly?

Considering I know just enough to read Groovy and barely understand it, I wouldn't be surprised if the error was a very simple one...

Updated code:


import groovy.json.JsonSlurper
import groovy.transform.Field

metadata {
    definition(name: "Delta Faucet Driver", namespace: "your.namespace", author: "Author Name") {
        capability "Switch"
    }
}

preferences {
    input("delta_device_id", "text", title: "Delta Device ID", description: "Enter Delta Device ID", defaultValue: "", required: true, displayDuringSetup: true)
//    input("delta_token", "text", title: "Delta Token", description: "Enter Delta Token", defaultValue: "", required: false, displayDuringSetup: true)
}

def installed() {
    log.debug "Installed"
}

def updated() {
    log.debug "Updated"
}

def on() {
    toggleWater("on")
}

def off() {
    toggleWater("off")
}

private toggleWater(String toggle) {
    def deviceId = delta_device_id
//    def deltaToken = "Bearer ${delta_token}"
    def apiUrl = "https://device.legacy.deltafaucet.com/api/device/toggleWater?deviceId=${deviceId}&toggle=${toggle}"
    def headers = [
        'Authorization': "Bearer 'ZXlKaGJHY2l…'",        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
        'Referer': 'https://device.legacy.deltafaucet.com/'
    ]
    
    try {
        def response
        httpPost(uri: apiUrl, headers: headers)
        
        if (response.status == 200) {
            def parsedResponse = new JsonSlurper().parseText(response.data)
            if (parsedResponse.success) {
                log.debug "Toggle water ${toggle} successful"
            } else {
                log.error "Toggle water ${toggle} failed: ${parsedResponse.error}"
            }
        } else {
            log.error "Toggle water ${toggle} failed: ${response.status}"
        }
    } catch (Exception e) {
        log.error "Error toggling water: ${e.message}"
    }
}

You missed the lines below httpPost in my message, and that closure is what is missing which leads to the error you saw.

1 Like

Oh, I didn’t realize I needed to add that as well - oups!

Added and you were right, it fixed that error!

It is working! Here is the full driver code. Now, any idea why I get the error when I try storing the Token?

Code that works:

import groovy.json.JsonSlurper
import groovy.transform.Field

metadata {
    definition(name: "Delta Faucet Driver", namespace: "your.namespace", author: "Author Name") {
        capability "Switch"
    }
}

preferences {
    input("delta_device_id", "text", title: "Delta Device ID", description: "Enter Delta Device ID", defaultValue: "", required: true, displayDuringSetup: true)
//    input("delta_token", "text", title: "Delta Token", description: "Enter Delta Token", defaultValue: "", xrequired: false, displayDuringSetup: true)
}

def installed() {
    log.debug "Installed"
}

def updated() {
    log.debug "Updated"
}

def on() {
    toggleWater("on")
}

def off() {
    toggleWater("off")
}

private toggleWater(String toggle) {
    def deviceId = delta_device_id
//    def deltaToken = "Bearer ${delta_token}"
    def apiUrl = "https://device.legacy.deltafaucet.com/api/device/toggleWater?deviceId=${deviceId}&toggle=${toggle}"
    def headers = [
        'Authorization': "Bearer ZXlKaGJHY….", 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
        'Referer': 'https://device.legacy.deltafaucet.com/'
    ]
    
    try {
        def response
        httpPost(uri: apiUrl, headers: headers)
        
        {
        resp ->
        response = resp
        }
        
        if (response.status == 200) {
            def parsedResponse = new JsonSlurper().parseText(response.data)
            if (parsedResponse.success) {
                log.debug "Toggle water ${toggle} successful"
            } else {
                log.error "Toggle water ${toggle} failed: ${parsedResponse.error}"
            }
        } else {
            log.error "Toggle water ${toggle} failed: ${response.status}"
        }
    } catch (Exception e) {
        log.error "Error toggling water: ${e.message}"
    }
}

Code that doesn’t work (changed lines 12, 33 and 36):


import groovy.json.JsonSlurper
import groovy.transform.Field

metadata {
    definition(name: "Delta Faucet Driver with Token Prompt", namespace: "your.namespace", author: "Author Name") {
        capability "Switch"
    }
}

preferences {
    input("delta_device_id", "text", title: "Delta Device ID", description: "Enter Delta Device ID", defaultValue: "", required: true, displayDuringSetup: true)
    input("delta_token", "text", title: "Delta Token", description: "Enter Delta Token", defaultValue: "", required: false, displayDuringSetup: true)
}

def installed() {
    log.debug "Installed"
}

def updated() {
    log.debug "Updated"
}

def on() {
    toggleWater("on")
}

def off() {
    toggleWater("off")
}

private toggleWater(String toggle) {
    def deviceId = delta_device_id
    def deltaToken = "Bearer ${delta_token}"
    def apiUrl = "https://device.legacy.deltafaucet.com/api/device/toggleWater?deviceId=${deviceId}&toggle=${toggle}"
    def headers = [
        'Authorization': deltaToken, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
        'Referer': 'https://device.legacy.deltafaucet.com/'
    ]
    
    try {
        def response
        httpPost(uri: apiUrl, headers: headers)
        
        {
        resp ->
        response = resp
        }
        
        if (response.status == 200) {
            def parsedResponse = new JsonSlurper().parseText(response.data)
            if (parsedResponse.success) {
                log.debug "Toggle water ${toggle} successful"
            } else {
                log.error "Toggle water ${toggle} failed: ${parsedResponse.error}"
            }
        } else {
            log.error "Toggle water ${toggle} failed: ${response.status}"
        }
    } catch (Exception e) {
        log.error "Error toggling water: ${e.message}"
    }
}

Error:

I’ve added the WIP code to GitHub:

1 Like

Try taking these out. They look like app data that bled over thanks to ChatGPT.

required: false, displayDuringSetup: true)

That's just a guess. Otherwise, I don't have any other theories. How many characters are in the token string?

2 Likes

Unfortunately, the error remains even with that removed.

Good questions for the characters! Lots… Let me see if I can figure it out…

What if you try to save just a short string; does it still fail?

1 Like

Good question! Yes - I tried: “ABC” and it worked. So that confirms an issue with the content… I assume size…

1 Like

Very odd. I'd probably ask one of the Hubitat engineers if there's an input length limit or else whether they can access any logs that give clues about what is causing the failure.

1 Like

Yea, that’s a good idea! :slight_smile:

1 Like

OK.
For communication between HE and HA on the HA side I am using "virtual devices".
I installed "virtual components" integration via HACS. I am sorry, this was long time ago and I forgot what exactly I installed. Please search HA Forum or Google how to install "virtual components" on HA. You may already have them installed on the HA. Beauty of using these
"virtual components" on HA - they are immediately visible on HE via HADB and control is
bi-directional.

Here is entry into the configuration.yaml file related to the Delta Faucet integration:

switch:
  - platform: virtual
    name: 'Kitchen Fauset Water On Switch'
    initial_value: 'off'
    initial_availability: true

  - platform: virtual
    name: 'Kitchen Fauset Water Off Switch'
    initial_value: 'off'
    initial_availability: true

input_text:
  delta_device_id:
    name: delta_device_id
    initial: !secret delta_device_id

rest_command: 
  delta_water_on:
    method: POST
    url: >
      https://device.legacy.deltafaucet.com/api/device/toggleWater?deviceId={{states.input_text.delta_device_id.state}}&toggle=on
    headers:
      Authorization: !secret delta_token
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
      Referer: https://device.legacy.deltafaucet.com/
  delta_water_off:
    method: POST
    url: >
      https://device.legacy.deltafaucet.com/api/device/toggleWater?deviceId={{states.input_text.delta_device_id.state}}&toggle=off
    headers:
      Authorization: !secret delta_token
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
      Referer: https://device.legacy.deltafaucet.com/

I am using two separate switches for turning water On and Off but sure, single switch could
be used.

Add these two lines into secrets.yaml file:

delta_device_id: <yours extracted device id string>
delta_token: "Bearer <yours extracted token 3 miles long string>"

And add this lines into automations.yaml file:

- id: '1685900902407'
  alias: Kitchen Faucet Water On
  description: Turn On Kitchen Delta Faucet
  trigger:
  - platform: state
    entity_id:
    - switch.virtual_kitchen_fauset_water_on_switch
    from: 'off'
    to: 'on'
  condition: []
  action:
  - service: rest_command.delta_water_on
    data: {}
  mode: single
- id: '1685901006499'
  alias: Kitchen Faucet Water Off
  description: Turn Off Kitchen Delta Faucet
  trigger:
  - platform: state
    entity_id:
    - switch.virtual_kitchen_fauset_water_off_switch
    from: 'off'
    to: 'on'
  condition: []
  action:
  - service: rest_command.delta_water_off
    data: {}
  mode: single

I am not sure if you can reuse the id codes but these automations is very easy
to create from GUI (I used GUI to create them).

If everything goes right on the HE side you should see these two HA virtual switches
related to the Kitchen Faucet and you should be able to control (On/Off) faucet.
Automations on the HE side is what you want.
just in case here is my RM rule for controlling Garbage Disposal:

HADB is installed on C7 and hub meshed to C8.
Actual RM rule is on C8

1 Like

Wow!
We are getting closer to get working HE driver for the Delta VoiceIQ fausets.
I hope this enormously long TOKEN will not be a show stopper.
I am glad, it works for HA but was afraid it could fail because of the size.

1 Like

Yea! The version of the code where the token is hard coded works. My facet turns on/off with the driver, so it’s just a question of getting the token in now, and making it so that it shows it’s on/off (currently, it doesn’t seem to generate states/events…?)

@tomw, I just counted the number of characters: 344

1 Like

Thanks. It is good to know the exact number. By necked eye it looked around 500.

1 Like

Right!?!

Note: The ‘ characters musn’t be included in the Token.

@Sebastien Thank you very much for the nice job!
At least hard coded TOKEN could be used. It is better than nothing.
But keeping this TOKEN in variable should not increase the TOKEN size.
Something I cannot clearly understand.

Yea, we have a good temporary solution. :blush:

I’ll do some more work on this tomorrow evening, if someone knows how the token can be entered in the main screen. I suspect that the code may need to be cleaned-up as well…

It successfully saves a text input of 255 characters but fails for >= 256. I suspect there's something in the internal code that can't accommodate something larger and is not handling the error nicely (hence the HTTP 500 error).

It feels like array index is defined as a character.
Is it possible to split input into two variables and then concatenate them at the end?