ChaseDurand / Pool-Pi

Hardware+software solution for controlling a Goldline/Hayward Aqualogic pool control board over WiFi via a Raspberry Pi.
Other
9 stars 7 forks source link

Pool/Spa/Spillover #3

Open rpaldrich opened 1 year ago

rpaldrich commented 1 year ago

I am having one issue. The pool/spa/spillover button passes me the following error; image

Any idea what my problem is? I didn't change any of the code except for the command.py file for specific commands serial data going to the controller?

I'm curious if this is caused by another issue I'm having. It seems pool-pi does not pass my controller correct serial commands to cycle from Filter On (high speed) to Filter On (low speed) to Filter Off. Perhaps this is part of the issue?

ChaseDurand commented 1 year ago

First I should clarify that this project has only been used with an AquaLogic unit emulating a GLX-LOCAL-PS-8 (theoretically expandable to 16) controller with a single speed pump. I'm not familiar with the timeline or compatibility of the protocol across other base systems (AquaPlus, ProLogic), controllers/interfaces (GLX-PL-LOC-P-4, AQL-SS-RF, etc), or 2 or variable speed pumps, but based on what I've seen online there is some overlap.

I have some ideas on warping this to work, but I won't be able to test because of our different hardware setups, so I'm somewhat flying blind :upside_down_face:

Based on the xlsx file you shared in #1, it looks like your setup only has pool/spa, no spillover (as an aside- the manual states that it does support a spillover which would be indicated by both LEDs being illuminated). My current setup uses the same button to cycle around pool->spa->spillover, so the system keeps sending the button until spillover mode is enabled (which never happens on your system) or it hits the message timeout (10 send attempts). To fix this, the logic that sets up the next expected state needs to be changed to only go between pool and spa in lines 139-162 in poolpi.js.

        if (buttonID == 'pool-spa-spillover') {
            // Need to check which pool/spa is lit
            // Pool<->Spa
            if (!(document.getElementById('pool-spa-spillover').parentElement.parentElement.children['pool'].children['led'].classList.contains('off'))) {
                // Pool is on, need spa on
                buttonID = 'spa'
                targetState = 'ON';
            }
            else if(!(document.getElementById('pool-spa-spillover').parentElement.parentElement.children['spa'].children['led'].classList.contains('off'))) {
                // Spa is on, need pool on
                buttonID = 'pool'
                targetState = 'ON';
            }
            else {
                // We haven't initialized yet
                targetState = 'INIT';
            }

        }

We can remove the parsing/updating for the spillover LED on the web interface by changing line 89 in poolpi.js:

           else if ((attributeName == 'pool') || (attributeName == 'spa')) {

We can remove the spillover LED from the web interface by removing lines 90-94 on index.html.

We'll also remove the spillover state and version from being transmit from the backend to the front end because we just removed the ability for our front end to parse it. Add jsonItems.pop('spillover') to model.py in toJSON.

To completely remove spillover from the backend, it would have to be removed from button_toggle (to prevent it from being added as a tracked parameter in the model) and the LED_MASK both in commands.py, and glancing at how I wrote the parseLEDs function in parsing.py it might need to be tweaked as well. Leaving it in for now shouldn't be an issue.

I think those above steps should allow the pool/spa button to work as expected.

Concerning the filter speeds: Filter high is LED solid, filter low is LED blinking, and filter off is off. It looks like pushing the button sends the same command x80008000 regardless of state, and the rotation is always high/low/off.

Add the following block at line 107 in poolpi.js to update the front end filter LED to support blinking.

            // Parse filter
            else if (attributeName == 'filter') {
                if (msgObj['filter'].state == 'ON') {
                    document.getElementById('filter').parentElement.children['led'].className = 'LED green' + ' toggle-element';
                }
                else if (msgObj['filter'].state == 'OFF') {
                    document.getElementById('filter').parentElement.children['led'].className = 'LED off' + ' toggle-element';
                }
                else if (msgObj['filter'].state == 'BLINK') {
                    document.getElementById('filter').parentElement.children['led'].className = 'LED green blink' + ' toggle-element';
                }
                else if (msgObj['filter'].state == 'INIT') {
                    document.getElementById('filter').parentElement.children['led'].className = 'LED off' + ' toggle-element';
                }
            }

Now we need to send the correct request to the backend based on the current state. Before the else { statement that is currently at line 178, add the following:

        else if (buttonID == 'filter') {
            // Need to check if on/off/blinking
            if (document.getElementById(buttonID).parentElement.children['led'].classList.contains('off')) {
                // Currently off, need on/high
                targetState = 'ON';
            }
            else if (document.getElementById(buttonID).parentElement.children['led'].classList.contains('blink')) {
                // Currently blinking/low, target off
                targetState = 'OFF';
            }
            else {
                // Currently on/high, need blinking/low
                targetState = 'BLINK';
            }
        }

As for what the above error is: the backend is trying to parse a message from the front end with parts id, data, version, and confirm, but isn't finding one or more of the elements, which causes message["id/data/version/confirm"] to return something of NoneType, which can't get concatenated and throws an error. However, I don't know why this would be occurring after only changing command.py.

Let me know if this works for you!

rpaldrich commented 1 year ago

Chase -- I gave it a shot, but ran out of time. Will check it out again this weekend.

rpaldrich commented 1 year ago

Chase -- been working on this for awhile now in my free time. I think I found where my error is coming from and I bet you know the solution basically immediately.

Front end (poolpi.js) sends buttonVersion as null anytime pool-spa-spillover button is pushed. This seems to be because in line 138ish, buttonVersion=this.getAttribute('version') is referencing the pool-spa-spillover container, which is not "flex" and does not have a version associated with it. The individual pool/spa/spillover containers DO have versions associated with them, and they are flex, but the source for the version is never updated, and so the backend cannot parse it. How do I update the version? Or can the version be tied to the pool-spa-spillover container?

image

ChaseDurand commented 1 year ago

Bad news: your observations are correct and I have no explanation. I could have sworn the pool/spa/spill buttons worked correctly but I just pulled an old version and they don't.

Good news: I coincidentally did some refactoring recently that fixed this issue. The system now only uses a single version for the entire state of the system instead of per parameter. However, I also moved the next state logic from the front end to the back end, which makes my comments about changing the filter and pool/spa from a month ago invalid. I believe my recent changes will currently work with your pool/spa only setup, just not the 2-speed motor.

Would you mind sharing your code with your changes? I think I'll be able to modify the logic in the back end for you. Also, feel free to reach out via my username at gmail if that communication medium works better for back and forth troubleshooting.

rpaldrich commented 1 year ago

Ok so what I did to make the pool/spa thing work was this; if (buttonID == 'pool-spa-spillover') { // Need to check which pool/spa/spillover is lit // Pool->Spa->Spillover if (!(document.getElementById('pool-spa-spillover').parentElement.parentElement.children['pool'].children['led'].classList.contains('off'))) { // Pool is on, need spa on buttonID = 'spa' buttonVersion='1' targetState = 'ON'; } and I added the "buttonVersion='1'" hard code to the spa, and if I ever use spillover I would do same.

The 2 speed pump motor is a weird one. I took your previous suggestions and incorporated, but it causes some cycling of the pump. Let me mess around with it for a bit. github works great -- it is linked to email anyway. I'm going to check out your new code and see how it works. Everything else in my version(s) is pretty much the same as yours, with exception of;

I slightly changed the commands because my controller serial command message is very slightly different. I added buttonVersion to the poolpi.js file as above I updated the model.py script to take into account the newer raspibian setup of my rpi zero. self.ser = serial.Serial(port='/dev/serial0', baudrate=19200, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_TWO) Other than that, everything else the same. Would need to check my walk-through for sure.