Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set Operation Mode and Temperature Target #28

Open
3 tasks done
Mark612 opened this issue May 12, 2023 · 12 comments
Open
3 tasks done

Set Operation Mode and Temperature Target #28

Mark612 opened this issue May 12, 2023 · 12 comments
Labels
enhancement New feature or request

Comments

@Mark612
Copy link

Mark612 commented May 12, 2023

Checklist

  • I have filled out the template to the best of my ability.
  • This only contains 1 feature request (if you have multiple feature requests, open one feature request for each feature request).
  • This issue is not a duplicate feature request of previous feature requests.

Is your feature request related to a problem? Please describe.

Additional control of the API if possible. Setting the modes and temperature target when setpoint is used.

Describe the solution you'd like

Add the ability through service call methods to set the operation mode and target temperature

service: water_heater.set_operation_mode
data:
entity_id: water_heater.aquanta_a_water_heater
operation_mode: setpoint

service: water_heater.set_temperature
data:
entity_id: water_heater.aquanta_a_water_heater
temperature: 130

Describe alternatives you've considered

Exposing a switch would be ideal.

Additional context

May reference:
https://github.com/alexanv1/customcomponents/tree/master/aquanta

@bmcclure bmcclure added the enhancement New feature or request label May 12, 2023
@bmcclure
Copy link
Owner

Thanks for the request! I agree this would be a great enhancement.

The challenge with this is that I'm using this package from pypi which doesn't expose these abilities currently. The other aquanta custom component linked to does offer this functionality, but it's doing it by making requests to HTTP endpoints manually instead of using a separate python package to handle it (which is the HA best practice).

I think the best solution will probably be to fork the pypi package, implement the enhancements into that, and either get the original developer to merge the changes in or release a new package on pypi. Then I can update this integration to make use of the new functionality.

I'll plan to work on these changes as time permits, I don't think it should be very difficult as long as the API that the pypi package connects to is either the same or offers similar functionality.

@Mark612
Copy link
Author

Mark612 commented May 13, 2023

Additionally, add set_performance_mode

@Mark612
Copy link
Author

Mark612 commented May 16, 2023

An update that may be useful to those using aquanta.

I have two water heaters. One is in away mode, temperature/manual mode, the other is intelligence with the 'efficient' setting.

I wrote a custom program using http requests for setting temperature or intelligence modes, and performance modes (low, efficient, high). Pushing the button will toggle the modes and set the temperature based on the input select.

Implemented using templates and 'custom:button-card'

One interesting note, is that your implementation doesn't preserve the temperature state, so the history graph is 'unavailable' several times. May need to implement a 'var' or similar to keep the last value then update.

image

@bmcclure
Copy link
Owner

Thanks for the additional info!

Out of curiosity, and in case it might help with the full implementation, would you be willing to share the details of the HTTP requests you're using to make those changes? I'm curious if they're using the same API endpoint as the integration or not.

It seems like we should make a separate issue for the statistics gaps to keep this one focused on adding the missing API functionality. I'll create one to continue that discussion.

@Mark612
Copy link
Author

Mark612 commented May 18, 2023

Out of curiosity, and in case it might help with the full implementation, would you be willing to share the details of the HTTP requests you're using to make those changes? I'm curious if they're using the same API endpoint as the integration or not.

Will do. I found a couple of bugs, and working through them. I implemented it with HTTP/python, and a combination of input_numbers, automation, command line sensors and scripts. Seeing if I can simplify it a bit more.

@Mark612
Copy link
Author

Mark612 commented May 27, 2023

Out of curiosity, and in case it might help with the full implementation, would you be willing to share the details of the HTTP requests you're using to make those changes? I'm curious if they're using the same API endpoint as the integration or not.

Here is the core code that seems to be working for me. I borrowed quite a bit from Alex's code, he built the original Aquanta API calls. I also 'import dill' to save the entire session then load it again, to reduce the number of login calls. Keeps the session cache.

You would need to get the device IDs for each water heater. I have two, and just hard coded them.

I can set temperature, or use intelligence mode. For the latter, I can set less, efficient, or most efficient. You wouldn't need to update the status again, as you have that in your code.


API_BASE_URL = "https://portal.aquanta.io/portal"
API_LOGIN_URL = f"{API_BASE_URL}/login"
API_SET_LOCATION_URL = f"{API_BASE_URL}/set/selected_location?locationId="

API_SETTINGS_URL = f"{API_BASE_URL}/get/settings"
API_GET_URL = f"{API_BASE_URL}/get"

API_SET_SETTINGS_URL = f"{API_BASE_URL}/set/advancedSettings"
API_EFFICIENCY_SELECTION = f"{API_BASE_URL}/set/efficiencySelection"

CONTROL_MODE_INTELLIGENCE = "Aquanta Intelligence"
CONTROL_MODE_TEMPERATURE = "Set Temperature"

WATER_HEATER_ID_A = 1
WATER_HEATER_ID_B = 2

VERBOSITY = 0

###################
###FUNCTIONS
###################

def check_response(response: requests.Response):
if response.status_code != 200:
logPrint(f'Operation failed, status = {response.status_code}, response = {response.text}', 1)

return

def login(username: str, password: str):

session = requests.Session()

login_data = dict(email=username, password=password, returnSecureToken=True)
verifyPasswordResponse = session.post("https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=AIzaSyBHWHB8Org9BWCH-YFzis-8oEbMaKmI2Tw", data=login_data)
check_response(verifyPasswordResponse)
logPrint(f'Received VerifyPassword response, status = {verifyPasswordResponse.status_code}, json = {verifyPasswordResponse.json()}')

idToken = dict(idToken = verifyPasswordResponse.json()["idToken"])
loginResponse = session.post(f"{API_BASE_URL}/login", data=idToken)
check_response(loginResponse)
logPrint(f'Received login response, status = {loginResponse.status_code}, json = {loginResponse.json()}')

return session

class Aquanta():

def __init__(self, username, password, location):
    self._username = username
    self._password = password
    self._location = location

    self._session = None
    self._login_time = None
    self._session = None
    self._data = {}
    self._settings = {}


def check_login(self):

   if self._session is None or (datetime.now() - self._login_time > timedelta(minutes = 30)):
      # Login again after 30 minutes
      self._session = login(self._username, self._password)
      self._login_time = datetime.now()
      self.set_location()
      logPrint("new session",0)
   else:
      logPrint("session continues!",0)


def update_location(self, location):
    self._location = location

def set_location(self):
    response = self._session.put(f"{API_SET_LOCATION_URL}{self._location}")
    logPrint(f'Set location to {self._location}, status = {response.status_code}')


def update(self):

    self.check_login()

    try:
        response = self._session.get(API_GET_URL)
        check_response(response)
        logPrint(f'Received {API_GET_URL} response, status = {response.status_code}, json = {response.json()}')
        self._data = response.json()

        response = self._session.get(API_SETTINGS_URL)
        check_response(response)

        logPrint(f'Received {API_SETTINGS_URL} response, status = {response.status_code}, json = {response.json()}')
        self._settings = response.json()

    except:
        logPrint(f'Update error, will try to login and retry on the next update interval.')
        self._session = None

@property
def state(self):
    return self._data

@property
def performance_mode(self):
    return self.state["efficiencySelection"]

@property
def aquanta_intelligence_active(self):
    return not self.settings["aquantaIntel"]

@property
def settings(self):
    return self._settings

@property
def target_temperature(self):
    return round(float(self.settings["setPoint"]) * 1.8 + 32)

def set_performance_mode(self, performance_mode: str):
    self.check_login()
    json = {"efficiencySelection": performance_mode}
    response = self._session.put(API_EFFICIENCY_SELECTION, json=json)
    logPrint(f'Performance mode set to {performance_mode}. Received {API_SET_SETTINGS_URL} response, status = {response.status_code}')
    check_response(response)

    self.update()

set target temperature or intelactive where temperature is ignored

intelactive: True sets aquaintelligence active, False sets temperature control

def set_target_temperature(self, target_temperature, intelactive: bool):
    self.check_login()

    json = {
                "aquantaIntel": not intelactive,
                "aquantaSystem": False,
                "setPoint": (target_temperature - 32) / 1.8
            }

    response = self._session.put(API_SET_SETTINGS_URL, json=json)
    logPrint(f'Target temperature set to {target_temperature}. Received {API_SET_SETTINGS_URL} response, status = {response.status_code}')
    check_response(response)

    self.update()

portal = "portal.aquanta.io/views/"
if setting == "state":
if value == "efficiency":
if my_device.aquanta_intelligence_active:
print(my_device.state["efficiencySelection"])
else:
print(my_device.target_temperature)
else:
print("Not implemented")

elif setting == "efficiency":
my_device.set_target_temperature(target_temperature, True)
if value == "Less":
my_device.set_performance_mode("Less Efficient")
elif value == "Most":
my_device.set_performance_mode("Most Efficient")
else:
my_device.set_performance_mode("Efficient")

elif setting == "temperature":
target_temperature = float(value)
my_device.set_target_temperature(target_temperature, False)

@mluckolson
Copy link

Found my way here from the Home Assistant forums. Implemented @bmcclure's awesome integration via HACS and noticed two interesting things: (1) the frequent "unavailable" interrupts (mentioned above); and (2) a good surprise that it appears I can control the thermostat/setpoint from the device (see control immediately below the boost/away toggles) even though Ben mentioned in the OP that only boost and away could be controlled:
Screenshot 2024-08-13 at 4 44 42 PM

I'm using the latest version from HACS -- what would you guys recommend to address these two issues? @Mark612's implementation above is square on what I'm trying to do, but I'm not sure if that code has been pulled in, or is available as a separate fork somehow?

Appreciate any guidance -- and again, thanks for the awesome work so far!

@Mark612
Copy link
Author

Mark612 commented Aug 18, 2024

Found my way here from the Home Assistant forums. Implemented @bmcclure's awesome integration via HACS and noticed
Appreciate any guidance -- and again, thanks for the awesome work so far!

I use the process I described above. Var variables to solve the issue of the Aquanta api timing out, to save the last known good value. I also used my own code, independently, to set other parameters not provided in the integration.

I hope to help with the integration to merge my code in the future.

@mluckolson
Copy link

mluckolson commented Sep 4, 2024

@Mark612 nicely done with your app. Is your app an HA custom component or a freestanding app or? Willing to share?

Also, I’ve been scouring the web trying to find the http syntax to control setpoint and other parameters. Sounds like you coded to the http calls — can you point me to a link or something where the syntax is documented? TIA…

@Mark612
Copy link
Author

Mark612 commented Sep 6, 2024

@Mark612 HA custom component or a freestanding app or? Willing to share?

Also, I’ve been scouring the web trying to find the http syntax to control setpoint and other parameters. Sounds like you coded to the http calls — can you point me to a link or something where the syntax is documented? TIA…

See my posts above, you can see I use a https and sessions library to access the API portal. I use this as a freestanding app, called 'aquanta' in my coding folder, then integrate the commands into home assistant.

command_line:

  • sensor:
    name: Aquanta A Performance
    scan_interval: 604800
    command_timeout: 21
    command: "/config/coding/aquanta a state efficiency"

  • sensor:
    name: Aquanta B Performance
    scan_interval: 604900
    command_timeout: 21
    command: "/config/coding/aquanta b state efficiency"

And as a command.yaml to set the efficiency or change the temperature. I have 2 water heaters:

aquanta_a_intelligence: /config/coding/aquanta a efficiency {{variables}}
aquanta_b_intelligence: /config/coding/aquanta b efficiency {{variables}}
aquanta_a_settemp: /config/coding/aquanta a temperature {{variables}}
aquanta_b_settemp: /config/coding/aquanta b temperature {{variables}}

@mluckolson
Copy link

Ah I think I may have misunderstood. I thought you were saying that Aquanta supports http calls directly (e.g., http:/aquanta.io/user=xyz&tank=xyz&setpoint=125, or whatever syntax). Guess this explains why I couldn’t find documentation anywhere!

Since I’m an HA noob, I was going to hack a solution via http calls directly at least temporarily for setpoint temp and efficiency. Candidly I’m not sure where to start with your app code earlier in the thread, nor the commands immediately above. I will happily take any direction and aggressively try to push forward on my own if you’re willing to provide a startpoint?

Regardless, very much appreciate the reply.

@Mark612
Copy link
Author

Mark612 commented Sep 13, 2024

...Candidly I’m not sure where to start with your app code earlier in the thread, nor the commands immediately above. I will happily take any direction and aggressively try to push forward on my own if you’re willing to provide a startpoint?

Regardless, very much appreciate the reply.

I'll see if I can post my code up, and you can get it going. Even better, work with bmcclure to merge in the additional functionality needed, as described in this open issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants