Inkbird IBS-M1 with IBS-P01

Amazing @Nir , I have been trying to get pool temp for the last 18 months and here it is:

I have been looking around for an inexpensive option for pool temp, but most solutions were very expensive, except for InkBird, which had had some success with HA. I purchased 2x IBS-M1 on the hope of getting a gen 1 version, but I ended up with two gen 2 :frowning: I purchased an RTL dongle but couldn’t get that integrated either. Thank you…

I didn’t want to head down the complexity of ssh and the MQTT route, so I combined your two scripts into one python script that returns the value and then setup a simple command_line sensor.

- platform: command_line
  name: Pool Temperature
  command: 'python3 /config/scripts/get_pool_temp.py'
  unit_of_measurement: "°C"
  scan_interval: 60
#!/usr/bin/env python3
import json
import requests
import hmac
import hashlib
import time

# Declare constants
ClientID="<<ENTER CLIENT ID HERE>>"
ClientSecret="<<ENTER CLIENT SECRET HERE>>"
DeviceID = "<<ENTER DEVICE ID HERE>>"
ChannelID = "<<ENTER DEVICE CHANNEL HERE e.g. ch_1 or ch_rtd1"
BaseUrl = "https://openapi.tuyaeu.com"
EmptyBodyEncoded = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

# Character conversion dictionary for decoding the temperature value
charcter_conversion = {
    "A": 0, "E": 1, "I": 2, "M": 3, "Q": 4, "U": 5, "Y": 6, "c": 7,
    "g": 8, "k": 9, "o": 10, "s": 11, "w": 12, "0": 13, "4": 14, "8": 15
}

# Get current time in milliseconds
tuyatime = int(time.time()) * 1000

# Prepare signing for Access Token
URL = "/v1.0/token?grant_type=1"
StringToSign = f"{ClientID}{tuyatime}GET\n{EmptyBodyEncoded}\n\n{URL}"
AccessTokenSign = hmac.new(ClientSecret.encode(), StringToSign.encode(), hashlib.sha256).hexdigest().upper()

# Get Access Token
headers = {
    'client_id': ClientID,
    'sign_method': 'HMAC-SHA256',
    't': str(tuyatime),
    'sign': AccessTokenSign
}
response = requests.get(f"{BaseUrl}{URL}", headers=headers)
AccessToken = response.json()['result']['access_token']

# Send Device status request
URL = f"/v1.0/iot-03/devices/status?device_ids={DeviceID}"
StringToSign = f"{ClientID}{AccessToken}{tuyatime}GET\n{EmptyBodyEncoded}\n\n{URL}"
RequestSign = hmac.new(ClientSecret.encode(), StringToSign.encode(), hashlib.sha256).hexdigest().upper()

headers.update({
    'sign': RequestSign,
    'access_token': AccessToken
})

response = requests.get(f"{BaseUrl}{URL}", headers=headers)
RequestResponse = response.text

# Parse and process the request response
device_param = json.loads(RequestResponse)
number_of_status = len(device_param["result"][0]["status"])

for i in range(number_of_status):
    if device_param["result"][0]["status"][i]["code"] == ChannelID:
        raw_temp = device_param["result"][0]["status"][i]["value"]

# Decode the temperature value
raw_temp_first_charecter = raw_temp[1:2]
raw_temp_second_charecter = raw_temp[2:3]
raw_temp_third_charecter = raw_temp[3:4]

temp_by_ten = (ord(raw_temp_first_charecter) - 65)*16 + charcter_conversion[raw_temp_second_charecter]

if raw_temp_third_charecter == "B":
    temp_by_ten = temp_by_ten + 256

# Calculate and print the pool temperature
Pool_temp = temp_by_ten / 10
print(Pool_temp)

2 Likes

I did exactly the same :slight_smile:

But you are more lucky your code works for you :frowning:

For me only with my magic constant and only if the temp is > 26 (cca).

@keeema, this is strange. Generally the way Inkbird represents the temp is via 3 characters:

1st character is a capital letter between A and P. They use the unicode for the letter minus 65, times 16. Unicode of A is 65 so A is 0, B is 16, C is 32, etc.

2nd character is a letter or number, which is one of - A, E, I, M, Q, U, Y, c, g, k, o, s, w, 0, 4, 8. Each has a specific value, from 0 to 15 which is added to the first value. so AA is 0, AE is 1, AI is 2 to P8 which is 255. You divide this by 10 to get the temp, so P8 is 25.5

3rd character is either A or B - if it is A, you don’t need to change the number, if it is B, you need to add 256, which will give you tempt above 25.5. So AAB is 256 (25.6), AEB is 257 (25.7), etc. all the way to P8B - 511 (51.1)

In your first post, ch_rtd1 data was Aw8BAAAAAAAAXAD, so I used the AwB, which gives 0 + 12 + 256 = 268 (26.8). I am not sure what the 8 is doing there.

In your second post, ch_rtd1 is A/4AAAAAAAAAWwD, which doesn’t make sense to me because I have never seen the character “/” used.

In your last post, ch_rtd1 is AwUBAAAAAAAAWwD. Using AwB (ignoring the 3rd character) gives is 268.

[in retrospect, the above is incorrect - the leading A shouldn’t be used directly in the calculation, the 1st character (“w”, “/”) is used but in a different way that is described above, while the 2nd (“8”, “4”, “U”) and 3rd characters (“A” or “B”) seem to be used as described above]

What thermometer are you using? I am using a pool thermometer and the conversion may be different, especially given that you have a character in the 3rd place which I don’t have. It will be useful if you could record ch_rtd1 data and what is the corresponding temp systemically. What I did was to put the thermometer in a warm glass of water and let it cool, recording the data every time the temp changed. This is how I worked out the conversion. But I was able to do that because I am using a pool thermometer…

@markpurcell, your way makes much more sense! Can run straight from HA and is much much simpler. I am not a programmer - I just use examples I find on the internet to build what I need. I knew there is a simpler way to do it, and thought it can all run in python, but wasn’t sure how to convert the sh script I found to python (i.e. express the tuyatime and other strings in python, use the requests command instead of curl, etc), and didn’t want to spend more time after it was working. Great job - this will be easier to implement.

@marcel510, use the script and sensor markpurcell posted, it is much easier to implement than my scripts.

1 Like

My setup is IBS-P01B + IBS-M1 gen2.

I have a working solution.

Guys, don’t laugh and don’t ask me why…
@Nir what you wrote about how data are read and decoded on inkbird devices is completly right. My script is using it. BUT… I need to use two magic constants :man_facepalming::

#!/usr/bin/env python3
import json
import requests
import hmac
import hashlib
import time

debug = True

# Declare constants

ClientID = "clientid"
ClientSecret = "secret"
DeviceID = "deviceid"
ChannelID = "ch_rtd1"
BaseUrl = "https://openapi.tuyaeu.com"
EmptyBodyEncoded = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

# Character conversion dictionary for decoding the temperature value
charcter_conversion = {
    "A": 0, "E": 1, "I": 2, "M": 3, "Q": 4, "U": 5, "Y": 6, "c": 7,
    "g": 8, "k": 9, "o": 10, "s": 11, "w": 12, "0": 13, "4": 14, "8": 15
}

# Get current time in milliseconds
tuyatime = int(time.time()) * 1000

# Prepare signing for Access Token
URL = "/v1.0/token?grant_type=1"
StringToSign = f"{ClientID}{tuyatime}GET\n{EmptyBodyEncoded}\n\n{URL}"
AccessTokenSign = hmac.new(ClientSecret.encode(), StringToSign.encode(), hashlib.sha256).hexdigest().upper()

# Get Access Token
headers = {
    'client_id': ClientID,
    'sign_method': 'HMAC-SHA256',
    't': str(tuyatime),
    'sign': AccessTokenSign
}
response = requests.get(f"{BaseUrl}{URL}", headers=headers)
AccessToken = response.json()['result']['access_token']

# Send Device status request
URL = f"/v1.0/iot-03/devices/status?device_ids={DeviceID}"
StringToSign = f"{ClientID}{AccessToken}{tuyatime}GET\n{EmptyBodyEncoded}\n\n{URL}"
RequestSign = hmac.new(ClientSecret.encode(), StringToSign.encode(), hashlib.sha256).hexdigest().upper()

headers.update({
    'sign': RequestSign,
    'access_token': AccessToken
})

response = requests.get(f"{BaseUrl}{URL}", headers=headers)
RequestResponse = response.text

# if debug: 
#     print(f'RequestResponse is now {RequestResponse}')

# Parse and process the request response
device_param = json.loads(RequestResponse)
number_of_status = len(device_param["result"][0]["status"])

for i in range(number_of_status):
    if device_param["result"][0]["status"][i]["code"] == ChannelID:
        raw_temp = device_param["result"][0]["status"][i]["value"]

if debug:      
    print(raw_temp)
# Decode the temperature value
raw_temp_first_charecter = raw_temp[1:2]
raw_temp_second_charecter = raw_temp[2:3]
raw_temp_third_charecter = raw_temp[3:4]

temp_by_ten = (ord(raw_temp_first_charecter) - 65)*16 + charcter_conversion[raw_temp_second_charecter]

if raw_temp_third_charecter == "B":
    temp_by_ten = temp_by_ten + 256

# Calculate and print the pool temperature
Pool_temp = temp_by_ten / 10

if(debug):
    print("Original value:" , Pool_temp)

# Magic constant assign
if Pool_temp > 0:
    coef = 86.4
else:
    coef = -52.8
Pool_temp = round((temp_by_ten/10) - coef, 1)

print(Pool_temp)

This gives me real values:

A/YAAAAAAAAAWwD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AA==
Original value: -28.2
24.6
A/UAAAAAAAAAWwD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AA==
Original value: -28.3
24.5
A/QAAAAAAAAAWwD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AA==
Original value: -28.4
24.4
AwYBAAAAAAAAXAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AA==
Original value: 112.6
26.2
AxIBAAAAAAAAWwD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AA==
Original value: 113.8
27.4
AxYBAAAAAAAAWwD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AA==
Original value: 114.2
27.8
AxwBAAAAAAAAWwD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AA==
Original value: 114.8
28.4

@Nir if you figure out how is this possible or if there is something we are missing (e.g. some ref. value in other part the whole string)
I would be really curious… :slight_smile: :slight_smile: :slight_smile:

It looks like your device doesn’t use A to P as the first character. It seems that it uses other characters, depending if temp is 25.5 or under, or if it is 25.6 and above. In that case there should be two conditions (as you found out). According to the examples you sent me, it appears that there will be two for formulas:

In cases where the temps is 25.5 or lower:

temp_by_ten = (ord(raw_temp_first_charecter) - 32)*16 + charcter_conversion[raw_temp_second_charecter]

In cases where the temp is 25.6 or higher:

temp_by_ten = (ord(raw_temp_first_charecter) - 119)*16 + charcter_conversion[raw_temp_second_charecter]

It looks like you have an “A” before your value, while I have a “C”, perhaps indicating what type of first character calculation to use. However, there is one problem with this system - if you start counting from w (unicode 119) for temp above 25.5, there are only 8 further increments you can do before getting to unicode 127, which doesn’t have a letter assigned. This means that the highest temp that can be expressed will be 38.3, which doesn’t make sense. It will be useful to have more examples from your readings for temps < 23.9 and > 38.3. The second and third characters in your example seem to work like mine.

The following script should work for both cases (yours and mine) with the information we have so far:

#!/usr/bin/env python3
import json
import requests
import hmac
import hashlib
import time

# Declare constants
ClientID="<<ENTER CLIENT ID HERE>>"
ClientSecret="<<ENTER CLIENT SECRET HERE>>"
DeviceID = "<<ENTER DEVICE ID HERE>>"
ChannelID = "<<ENTER DEVICE CHANNEL HERE e.g. ch_1 or ch_rtd1"
BaseUrl = "https://openapi.tuyaeu.com"
EmptyBodyEncoded = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

# Character conversion dictionary for decoding the temperature value
charcter_conversion = {
    "A": 0, "E": 1, "I": 2, "M": 3, "Q": 4, "U": 5, "Y": 6, "c": 7,
    "g": 8, "k": 9, "o": 10, "s": 11, "w": 12, "0": 13, "4": 14, "8": 15
}

# Get current time in milliseconds
tuyatime = int(time.time()) * 1000

# Prepare signing for Access Token
URL = "/v1.0/token?grant_type=1"
StringToSign = f"{ClientID}{tuyatime}GET\n{EmptyBodyEncoded}\n\n{URL}"
AccessTokenSign = hmac.new(ClientSecret.encode(), StringToSign.encode(), hashlib.sha256).hexdigest().upper()

# Get Access Token
headers = {
    'client_id': ClientID,
    'sign_method': 'HMAC-SHA256',
    't': str(tuyatime),
    'sign': AccessTokenSign
}
response = requests.get(f"{BaseUrl}{URL}", headers=headers)
AccessToken = response.json()['result']['access_token']

# Send Device status request
URL = f"/v1.0/iot-03/devices/status?device_ids={DeviceID}"
StringToSign = f"{ClientID}{AccessToken}{tuyatime}GET\n{EmptyBodyEncoded}\n\n{URL}"
RequestSign = hmac.new(ClientSecret.encode(), StringToSign.encode(), hashlib.sha256).hexdigest().upper()

headers.update({
    'sign': RequestSign,
    'access_token': AccessToken
})

response = requests.get(f"{BaseUrl}{URL}", headers=headers)
RequestResponse = response.text

# Parse and process the request response
device_param = json.loads(RequestResponse)
number_of_status = len(device_param["result"][0]["status"])

for i in range(number_of_status):
    if device_param["result"][0]["status"][i]["code"] == ChannelID:
        raw_temp = device_param["result"][0]["status"][i]["value"]

# Decode the temperature value
raw_temp_lead_charecter = raw_temp[0:1]
raw_temp_first_charecter = raw_temp[1:2]
raw_temp_second_charecter = raw_temp[2:3]
raw_temp_third_charecter = raw_temp[3:4]

if raw_temp_lead_charecter == "C":
    temp_by_ten = (ord(raw_temp_first_charecter) - 65)*16 + charcter_conversion[raw_temp_second_charecter]

if raw_temp_lead_charecter == "A":
    if ord(raw_temp_first_charecter) <= 47:
        temp_by_ten = (ord(raw_temp_first_charecter) - 32)*16 + charcter_conversion[raw_temp_second_charecter]
    if ord(raw_temp_first_charecter) >= 119:
        temp_by_ten = (ord(raw_temp_first_charecter) - 119)*16 + charcter_conversion[raw_temp_second_charecter]    


if raw_temp_third_charecter == "B":
    temp_by_ten = temp_by_ten + 256

# Calculate and print the pool temperature
Pool_temp = temp_by_ten / 10
print(Pool_temp)  
1 Like

Hi @markpurcell . How did you manage to put the IBS M1S to DP mode?
On the tuya iot dashboard, it looks like that after switching it to DP mode it always rejects it.

I did have a similar issue, every time I switched to DP it switched back :frowning:

I gave up in the end and then came back to it a couple of days later and it was in DP mode!

I can only think it may take a little while for the change to stick.

We should check if DP state is actually necessary. I wonder if the script will work anyway.

Nir, Please check out GitHub - richierockskool/homebridge-inkbird-wifi-gateway: homebridge Inkbird wifi gateway and try to help us fit this into our code and where to put it? Thank you, we are stuck and can not get this working. This could be the solution. Rich

I am sorry but I wouldn’t even know where to start. I am not familiar with homebridge and have never worked with Java. Do you have the same problem with getting the temp in code (i.e. “CAwB////////”)? If so, you can just use the logic I described above to convert it to temperature in Celsius.

Hey guys, I wanna try the python scripts but my curL response has the status empty. Anyone saw this before? I guess my sensor is the second version too because the Tuya shows everything in chinese.

I thought this is happening because it can’t find the sensor but I have no way to check if it’s connected and to connect to Tuya you have to remove it from the Inkbird app.

image

Have you changed to DP instruction on Tuya IoT?

And check that you have the correct server. You can find a list of servers here: tuya-home-assistant/regions_dataCenters.md at 363bca835c7297c89f751bcc84f93c42b081d647 · tuya/tuya-home-assistant · GitHub

Yes, i’ve changed to DP instruction and it’s on the correct server. But, i’ve tried the query again right now and returned the status. Let’s check if the python script works now.

EDIT: I’m having this python error:

Traceback (most recent call last):
  File "/home/lgnunes/teste.py", line 66, in <module>
    temp_by_ten = (ord(raw_temp_first_charecter) - 65)*16 + charcter_conversion[raw_temp_second_charecter]
KeyError: '/'

Heres my status response, my channel is 1 or 2? Didn’t work for both.

    "status": [
      {
        "code": "app_add_device_cmd",
        "value": "MA=="
      },
      {
        "code": "g_scan_device",
        "value": "MA=="
      },
      {
        "code": "ch_para",
        "value": "AQMAPFoCAwBnEQMBAIgnBAMAihkFAwBKJgYDAdbfAAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//w=="
      },
      {
        "code": "ch_cfg1",
        "value": "WAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAg=="
      },
      {
        "code": "ch_cfg2",
        "value": "WAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAg=="
      },
      {
        "code": "ch_cfg3",
        "value": "WAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAg=="
      },
      {
        "code": "ch_cfg4",
        "value": "WAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAg=="
      },
      {
        "code": "ch_cfg5",
        "value": "WAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAlgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAJYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAACWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAg=="
      },
      {
        "code": "ch_alarm",
        "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
      },
      {
        "code": "ch_alarm_trig1",
        "value": 0
      },
      {
        "code": "alarm_notify_status",
        "value": false
      },
      {
        "code": "ch_scene1",
        "value": "AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfw=="
      },
      {
        "code": "ch_scene2",
        "value": "AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfwAAAAAAAAAAAH8AAAAAAAAAAAB/AAAAAAAAAAAAfw=="
      },
      {
        "code": "ch_rtd1",
        "value": "AP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAD1QAUBRQF//9kAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AA=="
      },
      {
        "code": "his_ask_cmd",
        "value": "MA=="
      },
      {
        "code": "his_answer",
        "value": "MA=="
      },
      {
        "code": "his_data",
        "value": "MA=="
      },
      {
        "code": "ch_name1",
        "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
      },
      {
        "code": "ch_name2",
        "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
      },
      {
        "code": "ch_name3",
        "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
      },
      {
        "code": "ch_name4",
        "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
      },
      {
        "code": "ch_name5",
        "value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
      },
      {
        "code": "ch_rtd2",
        "value": "AP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AAD//////////wAA//////////8AAP//////////AA=="
      },
      {
        "code": "his_data_time",
        "value": "MA=="
      },
      {
        "code": "ch_alarm_trig2",
        "value": 0
      },
      {
        "code": "his_alarm1",
        "value": "/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////w=="
      },
      {
        "code": "his_alarm2",
        "value": "/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////w=="
      }
    ],

@Nir @Keeema
I have bought a Inkbird IBS M2 with the pool temperature sensor IBS P02R, and i used the python script from Keeema to retrieve data and was successfully (just had to change channel_id to ch_1 and ch_2).
I successfully convert the temperature data from the ch_1, which is the temperature from the pool sensor, but I’m having troubles converting the temperature values from the IBS M2 gateway, the values converted are not correct

I suppose the way data is encoded in this device is different from yours.
Could you tell me your how did you found how to decode the temperature data, so i can try to decode my, because i dont have idea how to do it

here some examples of the data (ch_0)

AQgBXgIAEA4AZA==

AQkBTgIAEA4AZA==

does this make sense?

i will put the https response in here, just in case.

{“result”:[{“id”:“bf71b6920027724e2atwmg”,“status”:[{“code”:“temp_current”,“value”:-200},{“code”:“temp_unit_convert”,“value”:“c”},{“code”:“signal_strength”,“value”:-200},{“code”:“ch_para”,“value”:“AAEBAQABCAEWHgAAAP//AAAA//8AAAD//wAAAP//AAAA//8AAAD//wAAAP//AAAA//8=”},{“code”:“ch_0”,“value”:“AQkBTgIAEA4AZA==”},{“code”:“ch_1”,“value”:“CAYB////////ZA==”},{“code”:“ch_2”,“value”:“”},{“code”:“ch_3”,“value”:“”},{“code”:“ch_4”,“value”:“”},{“code”:“ch_5”,“value”:“”},{“code”:“ch_6”,“value”:“”},{“code”:“ch_7”,“value”:“”},{“code”:“ch_8”,“value”:“”},{“code”:“ch_9”,“value”:“”},{“code”:“ch0_alarm”,“value”:0},{“code”:“ch1_alarm”,“value”:0},{“code”:“ch2_alarm”,“value”:0},{“code”:“ch3_alarm”,“value”:0},{“code”:“ch4_alarm”,“value”:0},{“code”:“ch5_alarm”,“value”:0},{“code”:“ch6_alarm”,“value”:0},{“code”:“ch7_alarm”,“value”:0},{“code”:“ch8_alarm”,“value”:0},{“code”:“ch9_alarm”,“value”:0},{“code”:“ch_cfg”,“value”:“WAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAVgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAFYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAABWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAVgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAFYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAABWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAVgCIANYAiADnP9kAJz/ZAAAAAAAAAAAAAFYAiADWAIgA5z/ZACc/2QAAAAAAAAAAAABWAIgA1gCIAOc/2QAnP9kAAAAAAAAAAAAAQ==”},{“code”:“beep_status”,“value”:0},{“code”:“add_ch”,“value”:1},{“code”:“ch_setting_char”,“value”:0},{“code”:“alm_ch_rtd”,“value”:“AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA”}]}],“success”:true,“t”:1686563085648,“tid”:“c39738b4090511eebfbfa24bf1d21c99”}

@miguel.silvestre I suspect you are right. The conversion I found is likely unique for the pool sensor and not other sensors. It makes sense because pool temperature sensor will have different range than other sensors. i.e. it will only be above 0 degrees Celsius and will have no humidity data.

The way I found out how the conversion works is by studying it systematically and finding the patterns. I placed the thermometer in a warm glass of water and wrote the data from the sensor and the actual temperature. Then, every time the temperature changed by 0.1 of a degree I wrote those values again. Eventually I had enough values to figure out the pattern as I described above. I am not sure how you will have such fine control over non-pool sensors.

From you example, I suspect some similarities between the conversion methods, i.e. I suspect they use the unicode for Q and then an assigned value for ‘g’ and ‘k’ given that those are the same letters I found. What were the actual temperatures when the values were ‘AQgBXgIAEA4AZA==’ and ‘AQkBTgIAEA4AZA==’?

@lgnunes, I am not sure why but your data in ‘ch_rtd1’ is not like mine or other examples I saw. The relevant data in previous examples is in the beginning of the string whereas your data has no relevant data in the beginning. You do have a string ‘wAD1QAUBRQF’ later in ‘ch_rdt1’, but this string doesn’t make sense to me. Your ‘ch_rtd2’ doesn’t have any data.

Can you see the temperature correctly in the Inkbird app?

At the moment, I have no info on Inkbird app because there is no way to keep the sensor connected to Inkbird app and Tuya at the same time. But before i’ve connected to Tuya, the Inkbird app was showing the temperature correctly.

Is your sensor a pool thermometer? The script only works for those.