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

Decoding SLEquipmentConfigurationMessage #26

Open
parnic opened this issue Jun 8, 2020 · 11 comments
Open

Decoding SLEquipmentConfigurationMessage #26

parnic opened this issue Jun 8, 2020 · 11 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@parnic
Copy link
Owner

parnic commented Jun 8, 2020

@mikemucc @bshep @tomblevins
Figured I'd go ahead and make an issue for decoding this thing so nobody's duplicating work.

As a reminder, most of this is coming from decompiling the slconfig app with jadx + experimentation.

  • versionDataArray can be turned into a version integer like so: (this.versionDataArray[0] * 1000) + (this.versionDataArray[1])
  • controllerData's top 2 bits indicate how many "slave" devices there are (which I'm calling "secondary" devices): (this.controllerData & 0x11000000) >> 6. don't know about the rest of the bits (my controller returns a value of 2 for this)
  • flowDataArray is where I've found the most interesting info so far. This contains information for up to 8 pumps worth of flow data. 45 bits are reserved for each pump and 24 of those contain data for the up-to-8 circuits that each pump supports. I've decoded a given pump's bits like this so far (values are from my equipment):
0:128 128 if pump exists, 0 if no pump, i think
1:0  priming time (in minutes, 0-10)
2:2  pump type (0 = none, 1 = IntelliFloVF, 2 = IntelliFloVS, 3 = IntelliFloVSF)
3:0  ? (maybe "background filtering"? this option is grayed out for me in slconfig)
4:11 circuit id (for me this is my "floor cleaner" circuit)
5:12 circuit val upper 8 bits
6:6  circuit id (for me this is "pool")
7:10 circuit val upper 8 bits
8:1  circuit id (for me this is "spa")
9:11 circuit val upper 8 bits
10:0 circuit id
11:3 circuit val upper 8 bits
12:0 circuit id
13:3 circuit val upper 8 bits
14:0 circuit id
15:3 circuit val upper 8 bits
16:0 circuit id
17:3 circuit val upper 8 bits
18:0 circuit id
19:3 circuit val upper 8 bits
20:3 priming RPM upper 8 bits
21:28 circuit 1 (4) val lower 8 bits
22:90 circuit 2 (6) val lower 8 bits
23:184 circuit 3 (8) val lower 8 bits
24:232 circuit 4 (10) val lower 8 bits
25:232 circuit 5 (12) val lower 8 bits
26:232 circuit 6 (14) val lower 8 bits
27:232 circuit 7 (16) val lower 8 bits
28:232 circuit 8 (18) val lower 8 bits
29:232 priming RPM lower 8 bits
30:0 unused?
31:0 unused?
32:0 unused?
33:0 unused?
34:0 unused?
35:0 unused?
36:0 unused?
37:0 unused?
38:0 unused?
39:0 unused?
40:0 unused?
41:0 unused?
42:0 unused?
43:0 unused?
44:0 unused?

Circuit id's are decoded via SLControllerConfigMessage's bodyArray array, deviceId property.
The circuit and priming rpms are really strange with the upper and lower bits separated in the array. Valid values for RPMs is 400 - 3450 with the default value being 1000 (upper 8 bits = 3, lower 8 bits = 232).

I'm having trouble reconciling this information with what I'm seeing in the SLConfig code, though. PageIntelliFloPriming and PageIntelliFloFiltering are both doing things with the flow data array, but the indices they're using don't match up quite right with what I'm seeing from my equipment. Still working on all this.

@parnic
Copy link
Owner Author

parnic commented Jun 8, 2020

Quick-and-dirty first shot at interpreting circuit RPMs: https://gist.github.com/parnic/998f0b1d89a0fecc25df2dd1c1916c8e

Example usage:

    console.log(`pump 1 pool rpm=${config.getCircuitRPMs(0, 6)}`);
    console.log(`pump 2 waterfall rpm=${config.getCircuitRPMs(1, 8)}`);

Feedback appreciated.

@bshep
Copy link
Contributor

bshep commented Jun 8, 2020

Quick-and-dirty first shot at interpreting circuit RPMs: https://gist.github.com/parnic/998f0b1d89a0fecc25df2dd1c1916c8e

Example usage:

    console.log(`pump 1 pool rpm=${config.getCircuitRPMs(0, 6)}`);
    console.log(`pump 2 waterfall rpm=${config.getCircuitRPMs(1, 8)}`);

Feedback appreciated.

This seems to work for me, i tweaked the code to be:

    for (var i = 0; i < this.controllerConfig.bodyArray.length; i++) {
      let deviceId = this.controllerConfig.bodyArray[i].deviceId;
      let circuitName = this.controllerConfig.bodyArray[i].name;
      console.log('pump 1 ' + circuitName + ' rpm= ' + config.getCircuitRPMs(0, deviceId));
    }

I get:

pump 1 Spa rpm= 2600
pump 1 Feat. Pump rpm= 2900
pump 1 Air Blower rpm= 3000
pump 1 Pool Light rpm= 0
pump 1 Spa Light rpm= 0
pump 1 Pool rpm= 1600
pump 1 Water Feature rpm= 0
pump 1 Jets rpm= 3450
pump 1 High Speed rpm= 2800

Which is correct

@bshep
Copy link
Contributor

bshep commented Jun 8, 2020

Also of note byte0 = 64 for me, i only have one VS pump so maybe that is why ( maybe a sort of length field?)

@parnic
Copy link
Owner Author

parnic commented Jun 8, 2020

Also of note byte0 = 64 for me, i only have one VS pump so maybe that is why ( maybe a sort of length field?)

Interesting! The SLConfig log seems to deal with byte 0 like this, which seems nonsensical to me:

        byte byCircuit = poolConfig.getEquipconfig().getFlowDataArray().get(iOffset).byteValue();
        if ((byCircuit & 128) == 0) {
            byte byCircuit2 = (byte) (byCircuit & 63);
        }

This could be a decompilation or deobfuscation bug. Either way, I'm not sure that byte is actually important for anything (byCircuit isn't used anywhere after this and obviously neither is byCircuit2). Byte 2 seems to be the actually important one.

@bshep
Copy link
Contributor

bshep commented Jun 8, 2020

Also of note byte0 = 64 for me, i only have one VS pump so maybe that is why ( maybe a sort of length field?)

Interesting! The SLConfig log seems to deal with byte 0 like this, which seems nonsensical to me:

        byte byCircuit = poolConfig.getEquipconfig().getFlowDataArray().get(iOffset).byteValue();
        if ((byCircuit & 128) == 0) {
            byte byCircuit2 = (byte) (byCircuit & 63);
        }

This could be a decompilation or deobfuscation bug. Either way, I'm not sure that byte is actually important for anything (byCircuit isn't used anywhere after this and obviously neither is byCircuit2). Byte 2 seems to be the actually important one.

Hmmm, i looked at the code too and its unclear to me as well, it may not be important.

Interestingly i could not find the current speed/gpm/power use on that message, however I did find another message called GETPUMPSTATUS (msg 12584) that seems to have the needed info and also contains the set RPMs per circuit, unfortunately this message is not used in the android app so I'm guessing as to the meaning of the data (i interpreted everything as uint32, that may be wrong for the first couple of bytes):

Byte Range:Data
0-3:3  --> unknown
4-7:??--> unknown, sometimes value is 1 sometimes 4294967295 (FF FF FF FF)
8-12:1377  --> currently used watts
13-16:2800, --> currentl rpms
17-20:0, --> unknown
21-24:65, --> currentl GPM
25-28:255, --> unknown

Settings for different circuits starts at byte 29, each circuit is 12 bytes long
0-3:6 --> settings for CircuitId = 6
4-7:1600 --> set to 1600 RPM of GPM (see next byte),
8-12:1 --> 1 for RPM, 0 for GPM
(data repeats as above)
1, 2600,       1,  --> same as above for circuit 1
2, 2900,    1,--> same as above for circuit 2
9,       2800,    1,    --> same as above for circuit 9
3,    3000,          1,    --> same as above for circuit 3
8, 3450,       1,        --> same as above for circuit 8
132, 2200,    1,--> same as above for circuit 132 (freeze mode)
0,         30,    0 --> unknown (unused circuit maybe? since there should be 8 settings)

Hopefully the formatting is not confusing

See this gist for my current work on decoding the data, if I can figure out more on this or the EquipmentConfigurationMesage i'll post more:
https://gist.github.com/bshep/5d7968639238832bd20c2fbb6769d990.js

@bshep
Copy link
Contributor

bshep commented Jun 9, 2020

Just a random thought, maybe a bit OT, could we use the SLEquipmentConfigurationMessage as a way to 'backup' our settings? Although most of the code dealing with its data is too complex for me to understand at the moment, it seems that the Android app sometimes edits values in memory and then sends it back to the controller, and looking at the field names it seems to be a pretty complete list of settings for the controller.

@parnic
Copy link
Owner Author

parnic commented Jun 10, 2020

Yeah that could work. I'm not sure how complete this data is either. My slconfig app somehow wiped the circuits on my first pump while I was messing with it, but since I had a dump of one of these messages I was able to restore it all.

@bshep
Copy link
Contributor

bshep commented Jun 11, 2020

did some work on interpreting the valveDataArray, pretty much just ported the java code over and cleaned up some of the logic, TBH i'm not 100% sure what some of the checks are doing, but i do get appropriate data

Testing Needs:

  • testing on a dual body pool as i have no idea if that code works
  • testing on a system with a heat pump
  • testing on a system with solar

bshep@30dec79

@bshep
Copy link
Contributor

bshep commented Jun 11, 2020

Also I was able to get the following info on SensorDataArray, MiscDataArray, DelayDataArray:

SensorDataArray:

  • Byte 0
    • Bit1 - 1 if solar present for pool
    • Bit4 - 1 if solar present for spa
  • Byte 1
    • Bit1 - only valid if therma flo present if FALSE (byte2bit5), then is this 1 then therma flo COOL is present
  • Byte 2
    • Bit4 - 1 if solar heat pump present
    • Bit5 - 1 if therma flo is present ( no idea what this is )

DelayDataArray:

  • Byte 0
    • Bit 0 - 1 if Pump Cool Down active for pool
    • Bit 1 - 1 if Pump Cool Down active for spa
    • Bit 7 - 1 if 'pumpOff' should be true ( not clear what this does )

MiscDataArray:

  • Byte 3
    • Bit 0 - if 0 set 'itellichem' to false ( not sure how its set to true later )
  • Byte 4 - if not 0 then 'spa manual' is true

@bshep
Copy link
Contributor

bshep commented Jun 17, 2020

See PR #32 for implementation of above decoding.

Still a lot of work to be done for the rest.

@parnic parnic added enhancement New feature or request help wanted Extra attention is needed labels Jul 15, 2020
@michaelmaston
Copy link

Also of note byte0 = 64 for me, i only have one VS pump so maybe that is why ( maybe a sort of length field?)

Interesting! The SLConfig log seems to deal with byte 0 like this, which seems nonsensical to me:

        byte byCircuit = poolConfig.getEquipconfig().getFlowDataArray().get(iOffset).byteValue();
        if ((byCircuit & 128) == 0) {
            byte byCircuit2 = (byte) (byCircuit & 63);
        }

This could be a decompilation or deobfuscation bug. Either way, I'm not sure that byte is actually important for anything (byCircuit isn't used anywhere after this and obviously neither is byCircuit2). Byte 2 seems to be the actually important one.

Hmmm, i looked at the code too and its unclear to me as well, it may not be important.

Interestingly i could not find the current speed/gpm/power use on that message, however I did find another message called GETPUMPSTATUS (msg 12584) that seems to have the needed info and also contains the set RPMs per circuit, unfortunately this message is not used in the android app so I'm guessing as to the meaning of the data (i interpreted everything as uint32, that may be wrong for the first couple of bytes):

Byte Range:Data
0-3:3  --> unknown
4-7:??--> unknown, sometimes value is 1 sometimes 4294967295 (FF FF FF FF)
8-12:1377  --> currently used watts
13-16:2800, --> currentl rpms
17-20:0, --> unknown
21-24:65, --> currentl GPM
25-28:255, --> unknown

Settings for different circuits starts at byte 29, each circuit is 12 bytes long
0-3:6 --> settings for CircuitId = 6
4-7:1600 --> set to 1600 RPM of GPM (see next byte),
8-12:1 --> 1 for RPM, 0 for GPM
(data repeats as above)
1, 2600,       1,  --> same as above for circuit 1
2, 2900,    1,--> same as above for circuit 2
9,       2800,    1,    --> same as above for circuit 9
3,    3000,          1,    --> same as above for circuit 3
8, 3450,       1,        --> same as above for circuit 8
132, 2200,    1,--> same as above for circuit 132 (freeze mode)
0,         30,    0 --> unknown (unused circuit maybe? since there should be 8 settings)

Hopefully the formatting is not confusing

See this gist for my current work on decoding the data, if I can figure out more on this or the EquipmentConfigurationMesage i'll post more:
https://gist.github.com/bshep/5d7968639238832bd20c2fbb6769d990.js

Based on some digging I was doing around the list and number of pumps, I believe that value of 64 indicates an IntelliFlo VSF pump. 0 is no pump, 128 is VS and 6 is VF. I just opened a case about getNumPumps() as it returns 8 pumps for me when I only have 1. By using byte0 of each 45 byte pump record in the FlowDataArray, you can determine if there really is a pump there or not. I added a 2nd pump in SLConfig to test even though I don't have a 2nd pump and the 2nd pump record started reporting its pump type (per the values I mentioned above) in byte0. No idea why they report different values for pump type in the FlowDataArray bytes vs. what you get in the pumpstatus message but I'm pretty sure of the mapping.

If your one pump is an IntelliFlo VSF (like mine) you should see 64 in that first byte of the first record.

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

No branches or pull requests

3 participants