dhmsjs / pyjacuzzi

Python interface to Jacuzzi brand spas via WiFi (e.g. using Prolink, not SmartTub)
Apache License 2.0
6 stars 0 forks source link

Proposal for REST API Integration and Collaboration #2

Open timmcfadden opened 8 months ago

timmcfadden commented 8 months ago

I've been exploring your project and am genuinely impressed with the work you've accomplished. The concept has been on my mind for quite some time, especially considering the limitations and potential obsolescence of existing iPhone and Android applications in this domain.

As I delved into your code, I found many aspects aligning with my understanding, despite not being primarily a Python developer. My background involves various programming languages and some experience with Python scripting.

I'm reaching out to propose an enhancement to your project: the development of a REST API. I believe this addition could significantly expand the project's utility, allowing for the creation of a WebUI wrapper. This approach aligns well with my personal experience, as I've developed a web dashboard for a HotTub system using a Raspberry Pi and sensors.

The integration of your code with a web-based interface could offer versatile and user-friendly applications. I am eager to explore the possibility of integrating your work into my existing projects.

Would you be open to collaborating on this endeavor? I am excited about the potential synergy between our skill sets and the enhancements we could bring to your project.

Looking forward to your thoughts and hoping for a positive collaboration.

Best regards,

Tim McFadden

HotTub
dhmsjs commented 8 months ago

Hi Tim,

Thanks for the kind words. Collaboration? Sure...with the normal caveats that the amount of effort I can provide may ebb or flow depending on a lot of things other than this.

As you may have seen I am also relatively new to Python, and even Github. Been in hardware and software development generally for more than a half century (yikes -- scary!). So much of my experience has been way before both of those. But I do like solving puzzles and teasing out answers to difficult questions.

This project is definitely still active and I welcome contributions from others. From my limited knowledge it doesn't seem like a REST api would require a great effort. The code as it stands today works pretty well...when it actually connects to the hot tub.

The biggest problem I have right now is that when I connect through Jacuzzi's Prolink network interface that box occasionally and randomly refuses to respond. I have looked into it a bit but have yet to find a reliable solution. Hard to know exactly what the Prolink box is doing. Interestingly, the Prolink box does exactly the same thing with Jacuzzi's phone app.

So what that means for your web interface is that until that connectivity issue is resolved it won't be any more reliable than the jacuzziui.py console interface I am currently using.

Some others have had success replacing the Prolink hardware with a generic TCP/IP-to-RS485 box. That seems to me to be the best long-term solution. I personally can't make much progress on that because I am usually about a 4 hour drive away from my hot tub -- so tinkering with the hot tub insides is not an option.

I guess my question regarding your desire for a REST api would be: what do you need from me?

timmcfadden commented 8 months ago

Thank you for your openness to collaboration. I appreciate your candidness about the time constraints and your diverse experience in hardware and software development.

Regarding the connectivity issues, I initially suspected they were linked to the iOS app's dependency on Jacuzzi's server for user authentication. However, your project has illuminated that it's more of a local hardware challenge. To address this, I plan to enhance my network setup by installing a 2.4 GHz access point near my hot tub. Additionally, I'm considering repositioning the module to the opposite side of the hot tub, hoping this might improve connectivity.

In my research, I stumbled upon a document that might be relevant to our issue. Have you had a chance to review this?

spa2

As for the REST API, I'm keen to delve deeper into your code, particularly the mechanisms for sending and receiving messages. I'll spend some more time with the codebase to better understand these aspects and will reach out with more specific inquiries.

I'm also attaching a document that might be useful for our collaboration:

8784.pdf

Looking forward to working together and contributing to this exciting project.

dhmsjs commented 8 months ago

I'm pretty sure I have seen that doc and I know that WiFi signal strength is not the problem (at least with my tub). Because when it works, it works 100% and when it doesn't it fails 100%. There is no sporadic connection as would be typical with a weak signal.

I am suspicious that there is some Denial of Service protection or similar in the Prolink module, triggered by my Python code (and also the phone app) requesting connection too often, too fast. But that is just a theory.

FWIW my phone is an Android device.

dhmsjs commented 8 months ago

Here's a quick overview of the comm process:

The topside control panel connects to the spa control board via an RS485 serial comm bus. This is a "party line" type of bus in that multiple devices can be connected to that bus. Everyone receives all message packets; it is up to each device to decode the packet and decide whether or not it is the intended recipient. Also each device has to be careful not to send a packet when another device is also sending -- the collision will corrupt both messages.

The Prolink module is just another device on the RS485 bus, like the topside panel. The format of the message packets is unchanged as they are sent across the TCP/IP network. This is why a generic TCP/IP to RS485 module can work.

The Prolink box does do some initial negotiation to connect with the local WiFi access point etc, but otherwise it pretty much just passes the message packets through unchanged. It does however ignore message packets sent between the topside panel and the spa controller. Those never show up on the TCP/IP side. In contrast, a generic TCP/IP to RS485 box can show you all the traffic on the RS485 bus.

Once connected, the spa controller broadcasts status message packets constantly -- roughly every second. These status messages contain the stuff that you normally see on the topside panel e.g. actual and setpoint temps, pump status, time and date info, etc. To make it easier to see changes my code only displays a packet when it has changed.

To change something you send a command packet to the spa controller. There are many types of commands but essentially the Prolink just looks like another topside panel to the spa controller.

I used the basic comm send and receive code from balboa.py (written for balboa spas) because it was already debugged. There is a lot of overlap between the different spa makes (for instance the packet basic RS485 packet structure is the same across all makes) but the internal contents of a given packet type can vary quite a bit between makes -- thus the internal structure of balboa packets may be different from jacuzzi packets and different from sundance packets, etc.

balboa.py uses Python's asyncio library to handle the comm send and receive asynchronously. If you are not familiar with that library it is similar to multitasking but simpler. At the level of a REST api that detail should be transparent I believe.

The basic operation of jacuzzi.py (and balboa.py) is to maintain an internal set of variables (class properties) that reflect the current state of the spa at all times. We parse incoming status packets and update those state variables to match the new state. Commands just get sent to the spa controller; we never assume a command was received by the controller. Instead we wait to see the result show up in a new status packet.

I may be getting out ahead of my skis a bit on implementing RESTful apis but I think you would view jacuzzi.py as the server and jacuzziui.py as a (console-based) client. To implement a web-based UI, jacuzziui.py would be a model for how your webpage UI should interact with jacuzzi.py. The api would either be a wrapper for jacuzzi.py, or just a published set of setter/getter methods in the JacuzziSpaWifi class itself.

Let me know if that makes any sense.

timmcfadden commented 8 months ago

I've spent some more time reviewing the code in greater depth. The detailed explanation you provided about the communication process is incredibly helpful, and I now have a better grasp of the overall architecture.

However, I'm finding the integration with cursesui somewhat perplexing, as it adds a layer of complexity to understanding the core functionalities. To gain a firmer footing, I'd like to start with something more fundamental. Could you guide me on how to establish a basic connection to the hot tub using jacuzzi.py? Specifically, I'm aiming to retrieve a single status, like the water temperature, and display it. This initial step would be instrumental in helping me navigate the rest of the process more confidently.

In line with your suggestion, I've ordered an RS485 module. I agree that addressing the connectivity stability is crucial, and I'm hopeful that this hardware change will yield a more reliable setup.

Also, in my quest to deepen my understanding, I came across this project: https://github.com/jackbrown1993/Jacuzzi-RS485. Have you seen this before? It appears to be relevant to our discussion, and I wonder if there might be insights or techniques there that could benefit our project.

I look forward to your guidance on making that initial connection and retrieving a basic status. Once I have that foundational understanding, I'm confident I can progress with the integration and development of the REST API.

timmcfadden commented 8 months ago

Nevermind figured it out and created a more basic script:

import asyncio import time import os import logging

try: import jacuzzi except ImportError: import pyjacuzzi as jacuzzi

DEFAULT_SPA_IP_ADDR = "192.168.2.100"

async def display_spa_info(spa): while True: os.system('cls' if os.name == 'nt' else 'clear') print("Current Time: ", time.asctime(time.localtime(time.time()))) print("Spa Time: ", spa.get_spatime_text()) print("Connection State: ", spa.get_connection_state_text()) print("Water Temp: ", spa.get_curtemp_text()) print("2nd Temp: ", spa.get_2ndtemp_text()) print("Messages: ", spa.get_messages()) await asyncio.sleep(5) # Update every 5 seconds

if name == "main": spa = jacuzzi.JacuzziSpaWifi(DEFAULT_SPA_IP_ADDR) loop = asyncio.get_event_loop() loop.create_task(spa.check_connection_status()) loop.create_task(spa.listen()) loop.run_until_complete(display_spa_info(spa))

dhmsjs commented 8 months ago

Great!

Yes I have connected with jackbrown1993. Others with helpful info might include: garbled1 (original author of balboa.py) HyperActiveJ (RS485 connection to sundance spa) pddc (worked with me on the encryption algorithm and has some RS485 module experience too)

jysaloma commented 1 month ago

Is there a way to create a simple python command to set temperature?

dhmsjs commented 1 month ago

Yes -- see change_settemp() in jacuzzi.py