[APP][Pro] Balboa

Pumps are missing when CMS service recovers, would it be possible to rescan the device for capabilities when Online state has recovered?

The Temperature range HIGH LOW setting has strange issue we tried to solve some time ago.

Temperature range:

  • HIGH should allow values 26.5C - 40.0C
  • LOW should allow values 10.0C - 37.0C
  1. Spa in in HIGH setting with target temperature 34C
    • CMS in Homey is happy
    • CMS Mobile app is happy
  2. Set LOW from CMS in Homey
    • CMS Mobile sees that range is set to LOW and target set temperature becomes 20C
    • CMS in Homey: Range is LOW, but target temperature does not update, stays at 34C
  3. Set target temperature from CMS Mobile to 22C
    • CMS in Homey updates correctly to 22C
  4. Set target temperature from CMS in Homey to 28C
    • CMS Mobile updates correctly to 28C
  5. Set HIGH from CMS in Homey
    • CMS Mobile drops target temperature to 10 and Range stays Low
    • CMS in Homey after update interval changes also back to Low
    • Something here fails, can not return to HIGH mode from CMS in Homey
  6. Set HIGH from CMS Mobile
    • CMS Mobile is happy with HIGH and the original target of 34C
    • CMS in Homey after update interval also shows HIGH, but target temperature as 40C.
  7. Set target temperature to 33C from CMS Mobile
    • CMS in Homey updates target to 33C

So with those actions, youā€™ll have fresh diag-report from me.

Iā€™ll have a look at that :slight_smile:

Should be possible.
Does a app restart fix it? :thinking:

Yes, app restart recovers them fine.

then I think it would be possible to fix that :slight_smile:

1 Like

Iā€™ll have a look at this tonight :slight_smile:
I might bother you with some data later :stuck_out_tongue:

1 Like

@OH2TH so Iā€™m really doubting if I implemented it correctly.

So your Spa returns these values:

desiredTemp: '33.0'
targetDesiredTemp: '33.2',
currentTemp: '34.0',

at that point the Heater was READY and tempRange on HIGH

Do you have a clue whatā€™s the difference between desiredTemp and targetDesiredTemp?

@OH2TH another example:

desiredTemp: '15.5',
targetDesiredTemp: '10.0',
currentTemp: '34.0',

at that point the Heater was READY and tempRange on LOW

Guess this is still correct, I spotted some issues in the app will change that :slight_smile:
Also I added automatic ranges for the target temperature. So even the round thermometer in Homey should change along :ok_hand:

New app update (TEST: 1.7.0):

Changelog:

NEW:
1: NEW: recover after API outage


FIXES:
1: FIX: temperature issues
2: FIX: login error (pairing)
3: FIX: wrong button handler for tempRange
4: FIX: dynamic thermostat min/max based on tempRange Low/High




If you find any issues, send a report via the mobile homey app

More - apps - ControlMySpa - settings wheel right top - send diagnostic report (put your email in the input field to make it easier)




You can install the test version by clicking the link above and press install

Donā€™t want to be part of this test version? Install the live version ā†’ ControlMySpa App for Homey | Homey

Will test how it behaves now.

As for the heater rest/ready mode, it is mode setting and doesnā€™t relate to readiness. Hereā€™s what one manual says:

Ready and Rest Modes
READY Mode will circulate water every 1/2 hour using Pump 1 Low to maintain a constant water temperature, heat as needed, and refresh the temperature display (polling).
REST Mode will only allow heating during programed filter cycles. Since polling does not occur, the temperature display may not show a current temperature until the filtration pump has been running for a minute or two.

This would probably be the better way to save energy, as the heater would only heat while the programmed filter cycle is running. That is where the filter schedule would use 2kW and only 200W. Keep in mind that it is possible to set one or two cycle a day and should have them happen fairly evenly.

If the spa is in REST mode, you wonā€™t receive the water temperature except only during filter cycle, as the water temp measurement happens in the heating unit.

And this is something both mobile app and Homey app now shows as null value / unknown.

This bit strange, as there is nothing like that in the Mobile app. Maybe Iā€™ll grab the app from github and do some homey app run live test to see what happens and where.

Did some Postman snooping on what is transferred between the mobile app and controlmyspa.com and findByUsername endpoint returns the currentState where there is only targetDesiredTemp when the Spa has been idle for a while and no current data is available.

desiredTemp and currentTemp appears shortly after there has been some activity on the Spa. Common rule CIRCULATION_PUMP is ON, the values are available. And after going OFF, the values are available for an unknown time will try to find out for how long.

Still donā€™t know why those two desiredTemps have different values. targetDesiredTemp seems to be the one that is set by a user from the app. And looks like desiredTemp is the value used by the spa as the thermostat value on the heating block.

However, when HIGH/LOW setting is changed there seems to be a interesting behaviour
desiredTemp is set to the last set thermostat value the HIGH or LOW range had and
targetDesiredTemp becomes 50F for LOW and 104F for HIGH setting, so it seems that it is needed to read desiredTemp from the device, and then use setDesiredTemp to just write it back when range is changed.

desiredTemp looks like being the saved value in the controller for the HIGH and LOW modes, however only readable when Spa is still active. targetDesiredTemp looks like is not saved on the controller, and that is why the value for is at the ranges lowest for LOW or highest for HIGH. However that is a value that is available all the time.

{
  "currentState":{
    "runMode":"Rest",
    "desiredTemp":"99.5",
    "targetDesiredTemp":"100.5",
    "currentTemp":"94.1",
    "controllerType":"NGSC",
    "errorCode":38,
    "shouldShowAlert":false,
    "cleanupCycle":false,
    "uplinkTimestamp":"2023-01-13T05:24:00.912+0000",
    "staleTimestamp":"2023-01-13T05:25:45.912+0000",
    "heaterMode":"REST",
    "hour":7,
    "minute":23,
    "online":true,
    "celsius":true,
    "demoMode":false,
    "timeNotSet":false,
    "settingsLock":false,
    "spaOverheatDisabled":false,
    "bluetoothStatus":"NOT_PRESENT",
    "updateIntervalSeconds":0,
    "wifiUpdateIntervalSeconds":0,
    "components":[
      {
        "componentType":"HEATER",
        "materialType":"HEATER",
        "port":"0",
        "value":"OFF",
        "name":"HEATER",
        "registeredTimestamp":"2023-01-13T05:24:00.912+0000"
      },
      {
        "componentType":"FILTER",
        "materialType":"FILTER",
        "port":"0",
        "value":"OFF",
        "availableValues":[
          "OFF",
          "ON",
          "DISABLED"
        ],
        "name":"FILTER",
        "registeredTimestamp":"2023-01-13T05:24:00.912+0000",
        "hour":10,
        "durationMinutes":60
      },
      {
        "componentType":"FILTER",
        "materialType":"FILTER",
        "port":"1",
        "value":"OFF",
        "availableValues":[
          "OFF",
          "ON",
          "DISABLED"
        ],
        "name":"FILTER",
        "registeredTimestamp":"2023-01-13T05:24:00.912+0000",
        "hour":22,
        "durationMinutes":60
      },
      {
        "componentType":"OZONE",
        "materialType":"OZONE",
        "value":"OFF",
        "availableValues":[
          "OFF",
          "ON"
        ],
        "name":"OZONE",
        "registeredTimestamp":"2023-01-13T05:24:00.912+0000"
      },
      {
        "componentType":"PUMP",
        "materialType":"PUMP",
        "port":"0",
        "value":"OFF",
        "targetValue":"OFF",
        "availableValues":[
          "OFF",
          "HIGH"
        ],
        "name":"PUMP",
        "registeredTimestamp":"2023-01-13T05:24:00.912+0000"
      },
      {
        "componentType":"PUMP",
        "materialType":"PUMP",
        "port":"1",
        "value":"OFF",
        "targetValue":"OFF",
        "availableValues":[
          "OFF",
          "HIGH"
        ],
        "name":"PUMP",
        "registeredTimestamp":"2023-01-13T05:24:00.912+0000"
      },
      {
        "componentType":"PUMP",
        "materialType":"PUMP",
        "port":"2",
        "value":"OFF",
        "targetValue":"OFF",
        "availableValues":[
          "OFF",
          "HIGH"
        ],
        "name":"PUMP",
        "registeredTimestamp":"2023-01-13T05:24:00.912+0000"
      },
      {
        "componentType":"CIRCULATION_PUMP",
        "materialType":"CIRCULATION_PUMP",
        "value":"OFF",
        "availableValues":[
          "OFF",
          "HIGH"
        ],
        "name":"CIRCULATION_PUMP",
        "registeredTimestamp":"2023-01-13T05:24:00.912+0000"
      },
      {
        "componentType":"LIGHT",
        "materialType":"LIGHT",
        "port":"0",
        "value":"OFF",
        "targetValue":"OFF",
        "availableValues":[
          "OFF",
          "HIGH"
        ],
        "name":"LIGHT",
        "registeredTimestamp":"2023-01-13T05:24:00.912+0000"
      },
      }
    ],
    "setupParams":{
      "lowRangeLow":50,
      "lowRangeHigh":99,
      "highRangeLow":80,
      "highRangeHigh":104,
      "gfciEnabled":false,
      "drainModeEnabled":false,
      "lastUpdateTimestamp":"1970-01-01T00:00:03.498+0000"
    },
    "ethernetPluggedIn":true,
    "rs485ConnectionActive":true,
    "rs485AcquiredAddress":17,
    "messageSeverity":2,
    "uiCode":0,
    "uiSubCode":1,
    "invert":false,
    "allSegsOn":false,
    "panelLock":false,
    "military":true,
    "tempRange":"HIGH",
    "primingMode":false,
    "soundAlarm":false,
    "repeat":false,
    "panelMode":"PANEL_MODE_NGSC",
    "swimSpaMode":"SWIM_MODE_OTHER",
    "swimSpaModeChanging":false,
    "heaterCooling":false,
    "latchingMessage":false,
    "lightCycle":false,
    "elapsedTimeDisplay":false,
    "tvLiftState":0,
    "specialTimeouts":false,
    "stirring":false,
    "ecoMode":false,
    "soakMode":false,
    "overrangeEnabled":false,
    "heatExternallyDisabled":false,
    "testMode":false,
    "tempLock":false,
    "primaryTZLStatus":"TZL_NOT_PRESENT",
    "secondaryTZLStatus":"TZL_NOT_PRESENT",
    "secondaryFiltrationMode":"AWAY",
    "offlineAlert":false,
    "abdisplay":false
  },
  "salesDate":"2022-01-26T12:36:05.021+0000",
  "registrationDate":"2022-01-26T06:28:01.860+0000",
  "manufacturedDate":"2022-01-26T12:35:53.443+0000",
  "sold":"true",
  "owner":"RETRACTED JSON",
  "demo":false,
  "online":true,
  }
}

New feature request for API (setTime)

This API endpoint is not in the library but there is setTime available in the API. Would be nice to tell CMS to set the time to the current local time (there is no DST on CSM).

It takes in the body:

{"date":"01\/13\/2023","time":"07:25","military_format":"TRUE"}

where date is U.S. style mm/dd/yyyy and time hh:mm. However, date is actually ignores since the Spa doesnā€™t know anything about the calendar.

and responds with code 202 and json body return the time that was set in the values:

{
    "_id": "1234567890",
    "spaId": "1234567890",
    "requestTypeId": 16,
    "originatorId": "1234567890",
    "sentTimestamp": "2023-01-13T05:39:06.630+0000",
    "values": {
        "TIME_HOUR": "7",
        "TIME_MINUTE": "25",
        "MILITARY_FORMAT": "TRUE"
    },
    "metadata": {
        "Requested By": "my email address",
        "Via": "mobile"
    }
}

Interesting in the setTime API is that hh=24,48,72ā€¦ always means hour 00 of the day and does not effect date and 07:70 is equal to setting the time to 08:10. But maybe not try to do this :slight_smile:

Oh nice!
Where did you find this? :stuck_out_tongue:

Yes this is what I have in the app too now. So that should work the same.
See: com.balboa/drivers/main-device.js at main Ā· martijnpoppen/com.balboa Ā· GitHub

Actually tempRageLow / tempRangeHigh doesnā€™t change which value to use for target_temperature.

I think this could work better:

if (currentTemp) await this.setValue('measure_temperature', parseFloat(currentTemp), check, 10, settings.round_temp);
if (!desiredTemp) await this.setValue('target_temperature', parseFloat(targetDesiredTemp), check, 10, settings.round_temp);
if ((targetDesiredTemp === 104 || targetDesiredtemp === 50 ) && desiredTemp ) await this._controlMySpaClient.setTemp(desiredTemp);

I think that should catch the situation when HIGH/LOW is changed where targetDesiredTemp resets to one or the other temperature range ends.

If LOW was set, targetDesiredTemp becomes 50, and we want to setTemp ( desiredTemp ) to restore the current target_temperature
If HIGH was set, targetDesiredTemp becomes 104, and we want to setTemp ( desiredTemp ) to restore the current target_temperature

Does that make any sense?

Postman as a proxy, to sniff the traffic. Since it is available in the mobile app, I though that it must be somehow doable.

So had to learn how to setup Postman as proxy for https. Luckily the Mobile App respects the phoneā€™s proxy settings.

I get your point but the !desiredTemp will never run, as far as I saw itā€™s always coming back from the API.

Next to that, we need some conversion on the if ((targetDesiredTemp === 104 || targetDesiredtemp === 50 )

The library I use already does some conversions to celsius. Guess you got this data from the API itself?
Might also be that the library is adding the desiredTemp all the timeā€¦?

As I can see Postman, the API returns only targetDesiredTemp once the spa goes idle and doesnā€™t return the desiredTemp nor currentTemp values.

Looks like targetDesiredTemp could be ignored, and see how desiredTemp acts for target_temperature.

And indeed there is the problem, the library returns the value, even though API has nothing there.
maybe if we change the logic already in the library so, that if before celsius conversion it is checked that desiredTemp actually is there. If not, use targetDesiredTemp for the last saved value.

Then use only desiredTemp for target_temperature.

Hmm, setTempRange actually returns newSpaData with updated desiredTemp.
What if in

take the newSpaData.currentState.desiredTemp and setTemp that, this we the happens only when HIGH/LOW change is activated.