Judo water treatment

Thanks for your getjudo.py. Sadly does not work for me:

`

2025-01-09 15:02:07.037868 INFO AppDaemon: Calling initialize() for main_app
2025-01-09 15:02:07.324828 WARNING main_app: ------------------------------------------------------------
2025-01-09 15:02:07.326073 WARNING main_app: Unexpected error running initialize() for main_app
2025-01-09 15:02:07.327566 WARNING main_app: ------------------------------------------------------------
2025-01-09 15:02:07.333209 WARNING main_app: Traceback (most recent call last):
File “/usr/lib/python3.11/site-packages/appdaemon/app_management.py”, line 162, in initialize_app
await utils.run_in_executor(self, init)
File “/usr/lib/python3.11/site-packages/appdaemon/utils.py”, line 304, in run_in_executor
response = future.result()
^^^^^^^^^^^^^^^
File “/usr/lib/python3.11/concurrent/futures/thread.py”, line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/config/apps/main/main_entity.py”, line 8, in initialize
import getjudo
File “/config/apps/main/getjudo.py”, line 340, in
h_since_last_reg = entity(messages_getjudo.entities[21], “mdi:water-sync”, “sensor”, “h”) `

With my old getjudo.py there is the known error:
Autoconfigs wurden gesendet…

[‘Scriptfehler - Fehler beim Holen und Auswerten der Gerätedaten in Zeile: 380’, JSONDecodeError(‘Expecting value: line 1 column 1 (char 0)’)]

Which version of the implementation do you have? I use this one:

I saw that the files between different forks are sometimes a little bit different. Eventually this could be a reason.

funny. I started from scratch and used the original files and it is working again

1 Like

The same version (didn*t even know there are forks around :slight_smile: )

@mucki Eventually you can also try from scratch with the modifies getjudo.py I posted?

1 Like

Did you used the original getjudo.py too? Will try a fresh install later…

yes, the original getjudo.py
at least I get all values again.

thank you.
Here is mine, as there is an mistake in the send_command function

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import urllib3
import json
import time
import gc
import os
import sys
import config_getjudo
import messages_getjudo
import hashlib
import math
import requests
from paho.mqtt import client as mqtt
from datetime import date
import pickle
from threading import Timer
from dataclasses import dataclass

class entity():
    def __init__(self, name, icon, entity_type, unit = "", minimum = 1, maximum = 100, step = 1, value = 0):
        self.name = name
        self.unit = unit
        self.icon = icon
        self.entity_type = entity_type #total_inc, sensor, number, switch, 
        self.value = value
        self.minimum = minimum
        self.maximum = maximum
        self.step = step

    def send_entity_autoconfig(self):
        device_config = {
            "identifiers": f"[{client_id}]",
            "manufacturer": config_getjudo.MANUFACTURER,
            "model": config_getjudo.NAME,
            "name": client_id,
            "sw_version": config_getjudo.SW_VERSION
        }

        entity_config = {
            "device": device_config,
            "availability_topic": availability_topic,
            "payload_available": config_getjudo.AVAILABILITY_ONLINE,
            "payload_not_available": config_getjudo.AVAILABILITY_OFFLINE,
            "state_topic": state_topic,
            "name": client_id + " " + self.name,
            "unique_id": client_id + "_" + self.name,
            "icon": self.icon,
            "value_template": "{{value_json." + self.name + "}}"
        }

        if self.entity_type == "total_increasing":
            entity_config["device_class"] = "water"
            entity_config["state_class"] = "total_increasing"
            entity_config["state_class"] = self.entity_type
            entity_config["unit_of_measurement"] = self.unit
            self.entity_type = "sensor"

        elif self.entity_type == "number":
            entity_config["command_topic"] = command_topic
            entity_config["unit_of_measurement"] = self.unit
            entity_config["min"] = self.minimum
            entity_config["max"] = self.maximum
            entity_config["step"] = self.step
            entity_config["command_template"] = "{\"" + self.name + "\": {{ value }}}"

        elif self.entity_type == "switch":
            entity_config["command_topic"] = command_topic
            entity_config["payload_on"] = "{\"" + self.name + "\": 1}"
            entity_config["payload_off"] = "{\"" + self.name + "\": 0}"
            entity_config["state_on"] = 1
            entity_config["state_off"] = 0

        elif self.entity_type == "sensor":
            entity_config["unit_of_measurement"] = self.unit

        elif self.entity_type == "select":
            entity_config["command_topic"] = command_topic
            entity_config["command_template"] = "{\"" + self.name + "\": \"{{ value }}\"}"
            entity_config["options"] = self.unit

        else:
            print(messages_getjudo.debug[26])
            return

        autoconf_topic = f"homeassistant/{self.entity_type}/{config_getjudo.LOCATION}/{config_getjudo.NAME}_{self.name}/config"
        publish_json(client, autoconf_topic, entity_config)

    def parse(self, response, index, a,b):
        val = response["data"][0]["data"][0]["data"][str(index)]["data"]
        if val != "":
            self.value = int.from_bytes(bytes.fromhex(val[a:b]), byteorder='little')

class notification_entity():
    def __init__(self, name, icon, counter=0, value = ""):
        self.name = name
        self.icon = icon
        self.value = value
        self.counter = counter

    def send_autoconfig(self):
        device_config = {
            "identifiers": f"[{client_id}]",
            "manufacturer": config_getjudo.MANUFACTURER,
            "model": config_getjudo.NAME,
            "name": client_id,
            "sw_version": config_getjudo.SW_VERSION
        }
        entity_config = {
            "device": device_config,
            "availability_topic": availability_topic,
            "payload_available": config_getjudo.AVAILABILITY_ONLINE,
            "payload_not_available": config_getjudo.AVAILABILITY_OFFLINE,
            "state_topic": notification_topic,
            "name": client_id + " " + self.name,
            "unique_id": client_id + "_" + self.name,
            "icon": self.icon
        }
        autoconf_topic = f"homeassistant/sensor/{config_getjudo.LOCATION}/{config_getjudo.NAME}_{self.name}/config"
        publish_json(client, autoconf_topic, entity_config)

    def publish(self, message, debuglevel):
        self.value = message
        msg = str(self.value)
        print(msg)
        if config_getjudo.MQTT_DEBUG_LEVEL  >= debuglevel:
            client.publish(notification_topic, msg, qos=0, retain=True)

class Function_Caller(Timer):
    def run(self):
        while not self.finished.wait(self.interval):  
            self.function()

@dataclass
class savedata:
    day_today = 0
    offset_total_water = 0
    last_err_id = 0
    token = 0
    water_yesterday = 0
    da = 0
    dt = 0
    serial = 0
    reg_mean_time = 0
    reg_mean_counter = 1
    reg_last_val = 0
    reg_last_timestamp = 0
    total_softwater_at_reg = 0
    total_hardwater_at_reg = 0


def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print(messages_getjudo.debug[1])
        client.subscribe(command_topic)
        print(messages_getjudo.debug[2])
        
        client.publish(availability_topic, config_getjudo.AVAILABILITY_ONLINE, qos=0, retain=True)

        for obj in gc.get_objects():
            if isinstance(obj, entity):
                obj.send_entity_autoconfig()
        notify.send_autoconfig()
        print(messages_getjudo.debug[3])
    else:
        print(messages_getjudo.debug[4].format(rc))


#Callback
def on_message(client, userdata, message):
    print(messages_getjudo.debug[5].format(message.topic, message.payload))
    try:
        command_json = json.loads(message.payload)
        
        if output_hardness.name in command_json:
            if config_getjudo.USE_SODIUM_CHECK == True:
                sodium = round(((input_hardness.value - command_json[output_hardness.name]) * 8.2) + config_getjudo.SODIUM_INPUT,1)
                if  sodium < config_getjudo.SODIUM_LIMIT:
                    if send_command(str(60), int_to_le_hex(command_json[output_hardness.name], 8)):
                        notify.publish(messages_getjudo.debug[43].format(sodium, config_getjudo.SODIUM_LIMIT, command_json[output_hardness.name]), 2)
                else:
                    limited_hardness = input_hardness.value - ((config_getjudo.SODIUM_LIMIT - config_getjudo.SODIUM_INPUT)/8.2)
                    limited_hardness = math.ceil(limited_hardness) #round up
                    if send_command(str(60), int_to_le_hex(limited_hardness, 8)):
                        notify.publish(messages_getjudo.debug[44].format(limited_hardness), 2)
            else:
                set_value(output_hardness, 60, command_json[output_hardness.name], 8)
        elif regeneration_start.name in command_json:
                start_regeneration()


        if config_getjudo.USE_WITH_SOFTWELL_P == False:
            if salt_stock.name in command_json:
                set_value(salt_stock, 94,command_json[salt_stock.name]*1000, 16)
            
            elif water_lock.name in command_json:
                set_water_lock(command_json[water_lock.name])


            elif sleepmode.name in command_json:
                set_sleepmode(command_json[sleepmode.name])

            elif max_waterflow.name in command_json:
                set_value(max_waterflow, 75, command_json[max_waterflow.name], 16)

            elif extraction_time.name in command_json:
                set_value(extraction_time, 74, command_json[extraction_time.name], 16)

            elif extraction_quantity.name in command_json:
                set_value(extraction_quantity, 76, command_json[extraction_quantity.name], 16)

            elif holidaymode.name in command_json:
                set_holidaymode(command_json[holidaymode.name])

    except Exception as e:
        notify.publish([messages_getjudo.debug[27].format(sys.exc_info()[-1].tb_lineno),e], 3)


def publish_json(client, topic, message):
    json_message = json.dumps(message)
    result = client.publish(topic, json_message, qos=0, retain=True)


def set_water_lock(pos):
    if pos < 2:
        pos_index = str(73 - pos)
        if send_command(pos_index, ""):
            notify.publish(messages_getjudo.debug[7].format(pos), 2)
    else:
        print(messages_getjudo.debug[9])


def set_sleepmode(hours):
    if hours == 0:
        if send_command("73", ""):
            notify.publish(messages_getjudo.debug[10], 2)
    else:
        if send_command("171", str(hours)):
            notify.publish(messages_getjudo.debug[12].format(hours), 2)
        if send_command("171", ""):
            notify.publish(messages_getjudo.debug[14], 2)


def set_holidaymode(mode):
    if mode == messages_getjudo.holiday_options[1]:      #lock
        send_command("77", "9")
    elif mode == messages_getjudo.holiday_options[2]:    #mode1
        send_command("77", "3")
    elif mode == messages_getjudo.holiday_options[3]:    #mode2
        send_command("77", "5")
    else:                                               #off
        if send_command("73", ""):
            notify.publish(messages_getjudo.debug[40], 1)
        send_command("77", "0")


def start_regeneration():
    if send_command("65", ""):
        notify.publish(messages_getjudo.debug[16], 2)


def set_value(obj, index, value, length):
    if send_command(str(index), int_to_le_hex(value, length)):
        notify.publish(messages_getjudo.debug[18].format(obj.name, value), 2)


def send_command(index, data):
    try:
        headers = {
            'Connection': 'keep-alive',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'
        }
        cmd_response = requests.get(f"https://www.myjudo.eu/interface/?token={mydata.token}&group=register&command=write%20data&serial_number={mydata.serial}&dt={mydata.dt}&index={index}&data={data}&da={mydata.da}&role=customer", headers=headers)
        cmd_response_json = cmd_response.json()
        #cmd_response = http.request('GET', f"https://www.myjudo.eu/interface/?token={mydata.token}&group=register&command=write%20data&serial_number={mydata.serial}&dt={mydata.dt}&index={index}&data={data}&da={mydata.da}&role=customer")
        #cmd_response_json = json.loads(cmd_response.data)
        if "status" in cmd_response_json:
            if cmd_response_json["status"] == "ok":
                return True
    except Exception as e:
        notify.publish([messages_getjudo.debug[27].format(sys.exc_info()[-1].tb_lineno),e], 3)
        return False
    return False


def int_to_le_hex(integer,length):
    if length == 16:
        tmp = "%0.4X" % integer
        return (tmp[2:4] + tmp[0:2])
    elif length == 8:
        return ("%0.2X" % integer)
    else:
        notify.publish(messages_getjudo.debug[20], 3)


def judo_login(username, password):
    pwmd5 = hashlib.md5(password.encode("utf-8")).hexdigest()
    try:
        login_response = http.request('GET', f"https://www.myjudo.eu/interface/?group=register&command=login&name=login&user={username}&password={pwmd5}&nohash=Service&role=customer")
        login_response_json = json.loads(login_response.data)
        if "token" in login_response_json:
            print(messages_getjudo.debug[22].format(login_response_json['token']))
            return login_response_json['token']
        else:
            notify.publish(messages_getjudo.debug[21], 2)
            sys.exit()
    except Exception as e:
        notify.publish([messages_getjudo.debug[28].format(sys.exc_info()[-1].tb_lineno),e], 3)
        sys.exit()


#----- INIT ----
command_topic =f"{config_getjudo.LOCATION}/{config_getjudo.NAME}/command"
state_topic = f"{config_getjudo.LOCATION}/{config_getjudo.NAME}/state"
availability_topic = f"{config_getjudo.LOCATION}/{config_getjudo.NAME}/status"
notification_topic = f"{config_getjudo.LOCATION}/{config_getjudo.NAME}/notify"
client_id = f"{config_getjudo.NAME}-{config_getjudo.LOCATION}"

http = urllib3.PoolManager()
mydata = savedata()


#Setting up all entities for homeassistant
next_revision = entity(messages_getjudo.entities[0], "mdi:account-wrench", "sensor", "Tagen")
total_water = entity(messages_getjudo.entities[1], "mdi:water-circle", "total_increasing", "mÂł")
output_hardness = entity(messages_getjudo.entities[6], "mdi:water-minus", "number", "°dH", 1, 15)
input_hardness = entity(messages_getjudo.entities[7], "mdi:water-plus", "sensor", "°dH")
regenerations = entity(messages_getjudo.entities[10], "mdi:water-sync", "sensor")
regeneration_start = entity(messages_getjudo.entities[12], "mdi:recycle-variant", "switch")
water_today = entity(messages_getjudo.entities[14], "mdi:chart-box", "total_increasing", "L")
water_yesterday = entity(messages_getjudo.entities[15], "mdi:chart-box-outline", "sensor", "L")
notify = notification_entity(messages_getjudo.entities[16], "mdi:alert-outline")
h_since_last_reg = entity(messages_getjudo.entities[21], "mdi:water-sync", "sensor", "h")
avg_reg_interval = entity(messages_getjudo.entities[22], "mdi:water-sync", "sensor", "h")


if config_getjudo.USE_WITH_SOFTWELL_P == False:
    salt_stock = entity(messages_getjudo.entities[4], "mdi:gradient-vertical", "number", "kg", 1, 50)
    salt_range = entity(messages_getjudo.entities[5], "mdi:chevron-triple-right", "sensor", "Tage")
    total_softwater_proportion = entity(messages_getjudo.entities[2], "mdi:water-outline", "total_increasing", "mÂł")
    total_hardwater_proportion = entity(messages_getjudo.entities[3], "mdi:water", "total_increasing", "mÂł")
    water_flow = entity(messages_getjudo.entities[8], "mdi:waves-arrow-right", "sensor", "L/h")
    batt_capacity = entity(messages_getjudo.entities[9], "mdi:battery-50", "sensor", "%")
    water_lock = entity(messages_getjudo.entities[11], "mdi:pipe-valve", "switch")
    sleepmode = entity(messages_getjudo.entities[13], "mdi:pause-octagon", "number", "h", 0, 10)
    extraction_time = entity(messages_getjudo.entities[17], "mdi:clock-alert-outline", "number", "min", 10, config_getjudo.LIMIT_EXTRACTION_TIME, 10)
    max_waterflow = entity(messages_getjudo.entities[18], "mdi:waves-arrow-up", "number", "L/h", 500, config_getjudo.LIMIT_MAX_WATERFLOW, 500)
    extraction_quantity = entity(messages_getjudo.entities[19], "mdi:cup-water", "number", "L", 100, config_getjudo.LIMIT_EXTRACTION_QUANTITY, 100)
    holidaymode = entity(messages_getjudo.entities[20], "mdi:palm-tree", "select", messages_getjudo.holiday_options)
    mixratio = entity(messages_getjudo.entities[23], "mdi:tune-vertical", "sensor", "L")

try: 
    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
    if config_getjudo.USE_MQTT_AUTH:
        client.username_pw_set(config_getjudo.MQTTUSER, config_getjudo.MQTTPASSWD)
    client.will_set(availability_topic, config_getjudo.AVAILABILITY_OFFLINE, qos=0, retain=True)
    client.connect(config_getjudo.BROKER, config_getjudo.PORT, 60)
    client.loop_start()
except Exception as e:
    sys.exit(messages_getjudo.debug[33])


#Load stored variables:
print (messages_getjudo.debug[34])
print ("----------------------")
try:
    with open(config_getjudo.TEMP_FILE,"rb") as temp_file:
        mydata = pickle.load(temp_file)
    print (messages_getjudo.debug[35].format(mydata.last_err_id))
    print (messages_getjudo.debug[36].format(mydata.water_yesterday))
    water_yesterday.value = mydata.water_yesterday
    print (messages_getjudo.debug[37].format(mydata.offset_total_water))
    print (messages_getjudo.debug[38].format(mydata.day_today))
    print ("da: {}".format(mydata.da))
    print ("dt: {}".format(mydata.dt))
    print ("serial: {}".format(mydata.serial))
    print ("token: {}".format(mydata.token))
    print ("avergage regeneration interval: {}h".format(mydata.reg_mean_time))
    print ("counter for avg-calc: {}".format(mydata.reg_mean_counter))
    print ("last regenerations count: {}".format(mydata.reg_last_val))
    print ("timestamp of last regeneration: {}s".format(mydata.reg_last_timestamp))
    if config_getjudo.USE_WITH_SOFTWELL_P == False:
        print ("Softwater prop. since Regeneration: {}L".format(mydata.total_softwater_at_reg))
        print ("Hardwater prop. since Regeneration: {}L".format(mydata.total_hardwater_at_reg))

except Exception as e:
    notify.publish([messages_getjudo.debug[29].format(sys.exc_info()[-1].tb_lineno),e], 3)
    try:
        with open(config_getjudo.TEMP_FILE,"wb") as temp_file:
            pickle.dump(mydata, temp_file)
        notify.publish(messages_getjudo.debug[41], 3)
    except:
        notify.publish([messages_getjudo.debug[42].format(sys.exc_info()[-1].tb_lineno),e], 3)
        sys.exit()

if mydata.token == 0:
    mydata.token = judo_login(config_getjudo.JUDO_USER, config_getjudo.JUDO_PASSWORD)


avg_reg_interval.value = mydata.reg_mean_time


#----- Mainthread ----
def main():
    headers = {
    'Connection': 'keep-alive',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'
    }
    try:
        response = requests.get(f"https://www.myjudo.eu/interface/?token={mydata.token}&group=register&command=get%20device%20data", headers=headers)
        response_json = response.json()
        if response_json["status"] ==  "ok":
            #print("Parsing values from response...")
            mydata.serial = response_json["data"][0]["serialnumber"]
            mydata.da = response_json["data"][0]["data"][0]["da"]
            mydata.dt = response_json["data"][0]["data"][0]["dt"]

            next_revision.parse(response_json, 7, 0, 4)
            if config_getjudo.USE_WITH_SOFTWELL_P == False:
                total_water.parse(response_json, 8, 0, 8)
                salt_stock.parse(response_json,94, 0, 4)
                salt_range.parse(response_json,94, 4, 8)
                total_softwater_proportion.parse(response_json, 9, 0, 8)
                water_flow.parse(response_json, 790, 34, 38)
                batt_capacity.parse(response_json, 93, 6, 8)
                water_lock.parse(response_json, 792, 2, 4)
                sleepmode.parse(response_json,792, 20, 22)
                max_waterflow.parse(response_json, 792, 26, 30)
                extraction_quantity.parse(response_json, 792, 30, 34)
                extraction_time.parse(response_json, 792, 34, 38)
                holidaymode.parse(response_json,792, 38, 40)
            else:
                total_water.parse(response_json, 9, 0, 8)

            output_hardness.parse(response_json, 790, 18, 20)
            input_hardness.parse(response_json, 790, 54, 56)
            regenerations.parse(response_json, 791, 62, 66)
            regeneration_start.parse(response_json, 791, 2, 4)

            next_revision.value = int(next_revision.value/24)   #Calculation hours to days
            total_water.value =float(total_water.value/1000) # Calculating from L to mÂł

            if config_getjudo.USE_WITH_SOFTWELL_P == False:
                if holidaymode.value == 3:      #mode1
                    holidaymode.value = messages_getjudo.holiday_options[2]
                elif holidaymode.value == 5:    #mode2
                    holidaymode.value = messages_getjudo.holiday_options[3]
                elif holidaymode.value == 9:    #lock
                    holidaymode.value = messages_getjudo.holiday_options[1]
                else:                           #off
                    holidaymode.value = messages_getjudo.holiday_options[0]

                total_softwater_proportion.value = float(total_softwater_proportion.value/1000)# Calculating from L to mÂł
                total_hardwater_proportion.value = round((total_water.value - total_softwater_proportion.value),3)
                salt_stock.value /= 1000 
                if water_lock.value > 1:
                    water_lock.value = 1

            regeneration_start.value &= 0x0F
            if regeneration_start.value > 0:
                regeneration_start.value = 1


            today = date.today()
            #It's 12pm...a new day. Store today's value to yesterday's value and setting a new offset for a new count
            if today.day != mydata.day_today:
                mydata.day_today = today.day
                mydata.offset_total_water = int(1000*total_water.value)
                water_yesterday.value = water_today.value
                mydata.water_yesterday = water_today.value
            water_today.value = int(1000*total_water.value) - mydata.offset_total_water

            #Hours since last regeneration / Average regeneration interval
            if regenerations.value > mydata.reg_last_val:
                if (regenerations.value - mydata.reg_last_val) == 1: #Regeneration has started, 
                    if mydata.reg_last_timestamp != 0:
                        h_since_last_reg.value = math.ceil((int(time.time()) - mydata.reg_last_timestamp)/3600)
                        #neuer_mittelwert = ((counter-1)*alter_mittelwert + neuer_wert)/counter
                        avg_reg_interval.value = math.ceil(((mydata.reg_mean_counter-1)*mydata.reg_mean_time + h_since_last_reg.value)/mydata.reg_mean_counter)
                        mydata.reg_mean_time = avg_reg_interval.value
                        mydata.reg_mean_counter += 1
                    mydata.reg_last_timestamp = int(time.time()) 
                    mydata.reg_last_val = regenerations.value
                    if config_getjudo.USE_WITH_SOFTWELL_P == False:
                        mydata.total_softwater_at_reg = total_softwater_proportion.value
                        mydata.total_hardwater_at_reg = total_hardwater_proportion.value
                else:
                    mydata.reg_last_val = regenerations.value
            if mydata.reg_last_timestamp != 0:
                h_since_last_reg.value = int((int(time.time()) - mydata.reg_last_timestamp)/3600)

            #Mix ratio Soft:Hard since last regeneration
            if config_getjudo.USE_WITH_SOFTWELL_P == False:
                softwater_since_reg = total_softwater_proportion.value - mydata.total_softwater_at_reg
                hardwater_since_reg = total_hardwater_proportion.value - mydata.total_hardwater_at_reg
                if softwater_since_reg != 0 and hardwater_since_reg !=0:
                    totalwater_since_reg = softwater_since_reg +  hardwater_since_reg

                    if hardwater_since_reg < softwater_since_reg:
                        mixratio.value = "1:" + str(round(1/(hardwater_since_reg/totalwater_since_reg),2))
                    else:
                        mixratio.value = str(round(1/(softwater_since_reg/totalwater_since_reg),2)) + ":1"
                else:
                    mixratio.value = "unknown"


            #print("Publishing parsed values over MQTT....")
            outp_val_dict = {}
            for obj in gc.get_objects():
                if isinstance(obj, entity):
                    outp_val_dict[obj.name] = str(obj.value)
            publish_json(client, state_topic, outp_val_dict)

        elif response_json["status"] == "error":
            notify.counter += 1
            if response_json["data"] == "login failed":
                notify.publish(messages_getjudo.debug[23],3)
                mydata.token = judo_login(config_getjudo.JUDO_USER, config_getjudo.JUDO_PASSWORD)
            else:
                val = response_json["data"]
                notify.publish(messages_getjudo.debug[24].format(val),3)
                notify.counter += 1
        else:
            print(messages_getjudo.debug[25])
            notify.counter += 1
    except Exception as e:
        notify.publish([messages_getjudo.debug[31].format(sys.exc_info()[-1].tb_lineno),e],3)
        notify.counter += 1

    try:
        error_response = http.request('GET',f"https://myjudo.eu/interface/?token={mydata.token}&group=register&command=get%20error%20messages")
        error_response_json = json.loads(error_response.data)
        if error_response_json["data"] != [] and error_response_json["count"] != 0:
            if mydata.last_err_id != error_response_json["data"][0]["id"]:
                mydata.last_err_id = error_response_json["data"][0]["id"]

                timestamp = error_response_json["data"][0]["ts_sort"]
                timestamp = timestamp[:-7] + ": "

                if error_response_json["data"][0]["type"] == "w":
                    error_message = timestamp + messages_getjudo.warnings[error_response_json["data"][0]["error"]]
                    notify.publish(error_message, 1)
                elif error_response_json["data"][0]["type"] == "e":
                    error_message = timestamp + messages_getjudo.errors[error_response_json["data"][0]["error"]]
                    notify.publish(error_message, 1)
    except Exception as e:
        notify.publish([messages_getjudo.debug[30].format(sys.exc_info()[-1].tb_lineno),e], 3)
        notify.counter += 1

    try:
        with open(config_getjudo.TEMP_FILE,"wb") as temp_file:
            pickle.dump(mydata, temp_file)
    except Exception as e:
        notify.publish([messages_getjudo.debug[29].format(sys.exc_info()[-1].tb_lineno),e], 3)
        notify.counter += 1

    if notify.counter >= config_getjudo.MAX_RETRIES:
        notify.publish(messages_getjudo.debug[32].format(config_getjudo.MAX_RETRIES),1)
        sys.exit()
    else:
        notify.counter = 0
#---------------------

Function_Caller(config_getjudo.STATE_UPDATE_INTERVAL, main).start()

notify.publish(messages_getjudo.debug[39], 2)   #Init Complete


now everything seems to work again. even settings and switches

2 Likes

Very, very strange. I completely reinstalled everything and didn’t even copy my old config file (reentered everything). Of course, because of the Appdaemon version 16.x, I had to change the path to the tmp file again, but everything seems to be working again. Even with the very old getjudo.py.

Let’s see how long this time :slight_smile:

Now testing with your getjudo.py - still works :slight_smile:

Thank you for your code. I think the mistake in send_command was mine. I just copied the code I got and didn’t have checked. I changed also in my code.

I also saw that you don’t have the header part in def judo_login. Was that intentional?

1 Like

No. Thanks. I will change it. It is working, but the header part is the more safe approach

here again my file now with the headers in login procedure as well

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import urllib3
import json
import time
import gc
import os
import sys
import config_getjudo
import messages_getjudo
import hashlib
import math
import requests
from paho.mqtt import client as mqtt
from datetime import date
import pickle
from threading import Timer
from dataclasses import dataclass

class entity():
    def __init__(self, name, icon, entity_type, unit = "", minimum = 1, maximum = 100, step = 1, value = 0):
        self.name = name
        self.unit = unit
        self.icon = icon
        self.entity_type = entity_type #total_inc, sensor, number, switch, 
        self.value = value
        self.minimum = minimum
        self.maximum = maximum
        self.step = step

    def send_entity_autoconfig(self):
        device_config = {
            "identifiers": f"[{client_id}]",
            "manufacturer": config_getjudo.MANUFACTURER,
            "model": config_getjudo.NAME,
            "name": client_id,
            "sw_version": config_getjudo.SW_VERSION
        }

        entity_config = {
            "device": device_config,
            "availability_topic": availability_topic,
            "payload_available": config_getjudo.AVAILABILITY_ONLINE,
            "payload_not_available": config_getjudo.AVAILABILITY_OFFLINE,
            "state_topic": state_topic,
            "name": client_id + " " + self.name,
            "unique_id": client_id + "_" + self.name,
            "icon": self.icon,
            "value_template": "{{value_json." + self.name + "}}"
        }

        if self.entity_type == "total_increasing":
            entity_config["device_class"] = "water"
            entity_config["state_class"] = "total_increasing"
            entity_config["state_class"] = self.entity_type
            entity_config["unit_of_measurement"] = self.unit
            self.entity_type = "sensor"

        elif self.entity_type == "number":
            entity_config["command_topic"] = command_topic
            entity_config["unit_of_measurement"] = self.unit
            entity_config["min"] = self.minimum
            entity_config["max"] = self.maximum
            entity_config["step"] = self.step
            entity_config["command_template"] = "{\"" + self.name + "\": {{ value }}}"

        elif self.entity_type == "switch":
            entity_config["command_topic"] = command_topic
            entity_config["payload_on"] = "{\"" + self.name + "\": 1}"
            entity_config["payload_off"] = "{\"" + self.name + "\": 0}"
            entity_config["state_on"] = 1
            entity_config["state_off"] = 0

        elif self.entity_type == "sensor":
            entity_config["unit_of_measurement"] = self.unit

        elif self.entity_type == "select":
            entity_config["command_topic"] = command_topic
            entity_config["command_template"] = "{\"" + self.name + "\": \"{{ value }}\"}"
            entity_config["options"] = self.unit

        else:
            print(messages_getjudo.debug[26])
            return

        autoconf_topic = f"homeassistant/{self.entity_type}/{config_getjudo.LOCATION}/{config_getjudo.NAME}_{self.name}/config"
        publish_json(client, autoconf_topic, entity_config)

    def parse(self, response, index, a,b):
        val = response["data"][0]["data"][0]["data"][str(index)]["data"]
        if val != "":
            self.value = int.from_bytes(bytes.fromhex(val[a:b]), byteorder='little')

class notification_entity():
    def __init__(self, name, icon, counter=0, value = ""):
        self.name = name
        self.icon = icon
        self.value = value
        self.counter = counter

    def send_autoconfig(self):
        device_config = {
            "identifiers": f"[{client_id}]",
            "manufacturer": config_getjudo.MANUFACTURER,
            "model": config_getjudo.NAME,
            "name": client_id,
            "sw_version": config_getjudo.SW_VERSION
        }
        entity_config = {
            "device": device_config,
            "availability_topic": availability_topic,
            "payload_available": config_getjudo.AVAILABILITY_ONLINE,
            "payload_not_available": config_getjudo.AVAILABILITY_OFFLINE,
            "state_topic": notification_topic,
            "name": client_id + " " + self.name,
            "unique_id": client_id + "_" + self.name,
            "icon": self.icon
        }
        autoconf_topic = f"homeassistant/sensor/{config_getjudo.LOCATION}/{config_getjudo.NAME}_{self.name}/config"
        publish_json(client, autoconf_topic, entity_config)

    def publish(self, message, debuglevel):
        self.value = message
        msg = str(self.value)
        print(msg)
        if config_getjudo.MQTT_DEBUG_LEVEL  >= debuglevel:
            client.publish(notification_topic, msg, qos=0, retain=True)

class Function_Caller(Timer):
    def run(self):
        while not self.finished.wait(self.interval):  
            self.function()

@dataclass
class savedata:
    day_today = 0
    offset_total_water = 0
    last_err_id = 0
    token = 0
    water_yesterday = 0
    da = 0
    dt = 0
    serial = 0
    reg_mean_time = 0
    reg_mean_counter = 1
    reg_last_val = 0
    reg_last_timestamp = 0
    total_softwater_at_reg = 0
    total_hardwater_at_reg = 0


def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print(messages_getjudo.debug[1])
        client.subscribe(command_topic)
        print(messages_getjudo.debug[2])
        
        client.publish(availability_topic, config_getjudo.AVAILABILITY_ONLINE, qos=0, retain=True)

        for obj in gc.get_objects():
            if isinstance(obj, entity):
                obj.send_entity_autoconfig()
        notify.send_autoconfig()
        print(messages_getjudo.debug[3])
    else:
        print(messages_getjudo.debug[4].format(rc))


#Callback
def on_message(client, userdata, message):
    print(messages_getjudo.debug[5].format(message.topic, message.payload))
    try:
        command_json = json.loads(message.payload)
        
        if output_hardness.name in command_json:
            if config_getjudo.USE_SODIUM_CHECK == True:
                sodium = round(((input_hardness.value - command_json[output_hardness.name]) * 8.2) + config_getjudo.SODIUM_INPUT,1)
                if  sodium < config_getjudo.SODIUM_LIMIT:
                    if send_command(str(60), int_to_le_hex(command_json[output_hardness.name], 8)):
                        notify.publish(messages_getjudo.debug[43].format(sodium, config_getjudo.SODIUM_LIMIT, command_json[output_hardness.name]), 2)
                else:
                    limited_hardness = input_hardness.value - ((config_getjudo.SODIUM_LIMIT - config_getjudo.SODIUM_INPUT)/8.2)
                    limited_hardness = math.ceil(limited_hardness) #round up
                    if send_command(str(60), int_to_le_hex(limited_hardness, 8)):
                        notify.publish(messages_getjudo.debug[44].format(limited_hardness), 2)
            else:
                set_value(output_hardness, 60, command_json[output_hardness.name], 8)
        elif regeneration_start.name in command_json:
                start_regeneration()


        if config_getjudo.USE_WITH_SOFTWELL_P == False:
            if salt_stock.name in command_json:
                set_value(salt_stock, 94,command_json[salt_stock.name]*1000, 16)
            
            elif water_lock.name in command_json:
                set_water_lock(command_json[water_lock.name])


            elif sleepmode.name in command_json:
                set_sleepmode(command_json[sleepmode.name])

            elif max_waterflow.name in command_json:
                set_value(max_waterflow, 75, command_json[max_waterflow.name], 16)

            elif extraction_time.name in command_json:
                set_value(extraction_time, 74, command_json[extraction_time.name], 16)

            elif extraction_quantity.name in command_json:
                set_value(extraction_quantity, 76, command_json[extraction_quantity.name], 16)

            elif holidaymode.name in command_json:
                set_holidaymode(command_json[holidaymode.name])

    except Exception as e:
        notify.publish([messages_getjudo.debug[27].format(sys.exc_info()[-1].tb_lineno),e], 3)


def publish_json(client, topic, message):
    json_message = json.dumps(message)
    result = client.publish(topic, json_message, qos=0, retain=True)


def set_water_lock(pos):
    if pos < 2:
        pos_index = str(73 - pos)
        if send_command(pos_index, ""):
            notify.publish(messages_getjudo.debug[7].format(pos), 2)
    else:
        print(messages_getjudo.debug[9])


def set_sleepmode(hours):
    if hours == 0:
        if send_command("73", ""):
            notify.publish(messages_getjudo.debug[10], 2)
    else:
        if send_command("171", str(hours)):
            notify.publish(messages_getjudo.debug[12].format(hours), 2)
        if send_command("171", ""):
            notify.publish(messages_getjudo.debug[14], 2)


def set_holidaymode(mode):
    if mode == messages_getjudo.holiday_options[1]:      #lock
        send_command("77", "9")
    elif mode == messages_getjudo.holiday_options[2]:    #mode1
        send_command("77", "3")
    elif mode == messages_getjudo.holiday_options[3]:    #mode2
        send_command("77", "5")
    else:                                               #off
        if send_command("73", ""):
            notify.publish(messages_getjudo.debug[40], 1)
        send_command("77", "0")


def start_regeneration():
    if send_command("65", ""):
        notify.publish(messages_getjudo.debug[16], 2)


def set_value(obj, index, value, length):
    if send_command(str(index), int_to_le_hex(value, length)):
        notify.publish(messages_getjudo.debug[18].format(obj.name, value), 2)


def send_command(index, data):
    try:
        headers = {
            'Connection': 'keep-alive',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'
        }
        cmd_response = requests.get(f"https://www.myjudo.eu/interface/?token={mydata.token}&group=register&command=write%20data&serial_number={mydata.serial}&dt={mydata.dt}&index={index}&data={data}&da={mydata.da}&role=customer", headers=headers)
        cmd_response_json = cmd_response.json()
        #cmd_response = http.request('GET', f"https://www.myjudo.eu/interface/?token={mydata.token}&group=register&command=write%20data&serial_number={mydata.serial}&dt={mydata.dt}&index={index}&data={data}&da={mydata.da}&role=customer")
        #cmd_response_json = json.loads(cmd_response.data)
        if "status" in cmd_response_json:
            if cmd_response_json["status"] == "ok":
                return True
    except Exception as e:
        notify.publish([messages_getjudo.debug[27].format(sys.exc_info()[-1].tb_lineno),e], 3)
        return False
    return False


def int_to_le_hex(integer,length):
    if length == 16:
        tmp = "%0.4X" % integer
        return (tmp[2:4] + tmp[0:2])
    elif length == 8:
        return ("%0.2X" % integer)
    else:
        notify.publish(messages_getjudo.debug[20], 3)


def judo_login(username, password):
    pwmd5 = hashlib.md5(password.encode("utf-8")).hexdigest()
    try:
        headers = {
            'Connection': 'keep-alive',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'
        }
        login_response = requests.get(f"https://www.myjudo.eu/interface/?group=register&command=login&name=login&user={username}&password={pwmd5}&nohash=Service&role=customer", headers=headers)
        #login_response = http.request('GET', f"https://www.myjudo.eu/interface/?group=register&command=login&name=login&user={username}&password={pwmd5}&nohash=Service&role=customer")
        #login_response_json = json.loads(login_response.data)
        login_response_json = login_response.json()
        if "token" in login_response_json:
            print(messages_getjudo.debug[22].format(login_response_json['token']))
            return login_response_json['token']
        else:
            notify.publish(messages_getjudo.debug[21], 2)
            sys.exit()
    except Exception as e:
        notify.publish([messages_getjudo.debug[28].format(sys.exc_info()[-1].tb_lineno),e], 3)
        sys.exit()


#----- INIT ----
command_topic =f"{config_getjudo.LOCATION}/{config_getjudo.NAME}/command"
state_topic = f"{config_getjudo.LOCATION}/{config_getjudo.NAME}/state"
availability_topic = f"{config_getjudo.LOCATION}/{config_getjudo.NAME}/status"
notification_topic = f"{config_getjudo.LOCATION}/{config_getjudo.NAME}/notify"
client_id = f"{config_getjudo.NAME}-{config_getjudo.LOCATION}"

http = urllib3.PoolManager()
mydata = savedata()


#Setting up all entities for homeassistant
next_revision = entity(messages_getjudo.entities[0], "mdi:account-wrench", "sensor", "Tagen")
total_water = entity(messages_getjudo.entities[1], "mdi:water-circle", "total_increasing", "mÂł")
output_hardness = entity(messages_getjudo.entities[6], "mdi:water-minus", "number", "°dH", 1, 15)
input_hardness = entity(messages_getjudo.entities[7], "mdi:water-plus", "sensor", "°dH")
regenerations = entity(messages_getjudo.entities[10], "mdi:water-sync", "sensor")
regeneration_start = entity(messages_getjudo.entities[12], "mdi:recycle-variant", "switch")
water_today = entity(messages_getjudo.entities[14], "mdi:chart-box", "total_increasing", "L")
water_yesterday = entity(messages_getjudo.entities[15], "mdi:chart-box-outline", "sensor", "L")
notify = notification_entity(messages_getjudo.entities[16], "mdi:alert-outline")
h_since_last_reg = entity(messages_getjudo.entities[21], "mdi:water-sync", "sensor", "h")
avg_reg_interval = entity(messages_getjudo.entities[22], "mdi:water-sync", "sensor", "h")


if config_getjudo.USE_WITH_SOFTWELL_P == False:
    salt_stock = entity(messages_getjudo.entities[4], "mdi:gradient-vertical", "number", "kg", 1, 50)
    salt_range = entity(messages_getjudo.entities[5], "mdi:chevron-triple-right", "sensor", "Tage")
    total_softwater_proportion = entity(messages_getjudo.entities[2], "mdi:water-outline", "total_increasing", "mÂł")
    total_hardwater_proportion = entity(messages_getjudo.entities[3], "mdi:water", "total_increasing", "mÂł")
    water_flow = entity(messages_getjudo.entities[8], "mdi:waves-arrow-right", "sensor", "L/h")
    batt_capacity = entity(messages_getjudo.entities[9], "mdi:battery-50", "sensor", "%")
    water_lock = entity(messages_getjudo.entities[11], "mdi:pipe-valve", "switch")
    sleepmode = entity(messages_getjudo.entities[13], "mdi:pause-octagon", "number", "h", 0, 10)
    extraction_time = entity(messages_getjudo.entities[17], "mdi:clock-alert-outline", "number", "min", 10, config_getjudo.LIMIT_EXTRACTION_TIME, 10)
    max_waterflow = entity(messages_getjudo.entities[18], "mdi:waves-arrow-up", "number", "L/h", 500, config_getjudo.LIMIT_MAX_WATERFLOW, 500)
    extraction_quantity = entity(messages_getjudo.entities[19], "mdi:cup-water", "number", "L", 100, config_getjudo.LIMIT_EXTRACTION_QUANTITY, 100)
    holidaymode = entity(messages_getjudo.entities[20], "mdi:palm-tree", "select", messages_getjudo.holiday_options)
    mixratio = entity(messages_getjudo.entities[23], "mdi:tune-vertical", "sensor", "L")

try: 
    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
    if config_getjudo.USE_MQTT_AUTH:
        client.username_pw_set(config_getjudo.MQTTUSER, config_getjudo.MQTTPASSWD)
    client.will_set(availability_topic, config_getjudo.AVAILABILITY_OFFLINE, qos=0, retain=True)
    client.connect(config_getjudo.BROKER, config_getjudo.PORT, 60)
    client.loop_start()
except Exception as e:
    sys.exit(messages_getjudo.debug[33])


#Load stored variables:
print (messages_getjudo.debug[34])
print ("----------------------")
try:
    with open(config_getjudo.TEMP_FILE,"rb") as temp_file:
        mydata = pickle.load(temp_file)
    print (messages_getjudo.debug[35].format(mydata.last_err_id))
    print (messages_getjudo.debug[36].format(mydata.water_yesterday))
    water_yesterday.value = mydata.water_yesterday
    print (messages_getjudo.debug[37].format(mydata.offset_total_water))
    print (messages_getjudo.debug[38].format(mydata.day_today))
    print ("da: {}".format(mydata.da))
    print ("dt: {}".format(mydata.dt))
    print ("serial: {}".format(mydata.serial))
    print ("token: {}".format(mydata.token))
    print ("avergage regeneration interval: {}h".format(mydata.reg_mean_time))
    print ("counter for avg-calc: {}".format(mydata.reg_mean_counter))
    print ("last regenerations count: {}".format(mydata.reg_last_val))
    print ("timestamp of last regeneration: {}s".format(mydata.reg_last_timestamp))
    if config_getjudo.USE_WITH_SOFTWELL_P == False:
        print ("Softwater prop. since Regeneration: {}L".format(mydata.total_softwater_at_reg))
        print ("Hardwater prop. since Regeneration: {}L".format(mydata.total_hardwater_at_reg))

except Exception as e:
    notify.publish([messages_getjudo.debug[29].format(sys.exc_info()[-1].tb_lineno),e], 3)
    try:
        with open(config_getjudo.TEMP_FILE,"wb") as temp_file:
            pickle.dump(mydata, temp_file)
        notify.publish(messages_getjudo.debug[41], 3)
    except:
        notify.publish([messages_getjudo.debug[42].format(sys.exc_info()[-1].tb_lineno),e], 3)
        sys.exit()

if mydata.token == 0:
    mydata.token = judo_login(config_getjudo.JUDO_USER, config_getjudo.JUDO_PASSWORD)


avg_reg_interval.value = mydata.reg_mean_time


#----- Mainthread ----
def main():
    headers = {
    'Connection': 'keep-alive',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'
    }
    try:
        response = requests.get(f"https://www.myjudo.eu/interface/?token={mydata.token}&group=register&command=get%20device%20data", headers=headers)
        response_json = response.json()
        if response_json["status"] ==  "ok":
            #print("Parsing values from response...")
            mydata.serial = response_json["data"][0]["serialnumber"]
            mydata.da = response_json["data"][0]["data"][0]["da"]
            mydata.dt = response_json["data"][0]["data"][0]["dt"]

            next_revision.parse(response_json, 7, 0, 4)
            if config_getjudo.USE_WITH_SOFTWELL_P == False:
                total_water.parse(response_json, 8, 0, 8)
                salt_stock.parse(response_json,94, 0, 4)
                salt_range.parse(response_json,94, 4, 8)
                total_softwater_proportion.parse(response_json, 9, 0, 8)
                water_flow.parse(response_json, 790, 34, 38)
                batt_capacity.parse(response_json, 93, 6, 8)
                water_lock.parse(response_json, 792, 2, 4)
                sleepmode.parse(response_json,792, 20, 22)
                max_waterflow.parse(response_json, 792, 26, 30)
                extraction_quantity.parse(response_json, 792, 30, 34)
                extraction_time.parse(response_json, 792, 34, 38)
                holidaymode.parse(response_json,792, 38, 40)
            else:
                total_water.parse(response_json, 9, 0, 8)

            output_hardness.parse(response_json, 790, 18, 20)
            input_hardness.parse(response_json, 790, 54, 56)
            regenerations.parse(response_json, 791, 62, 66)
            regeneration_start.parse(response_json, 791, 2, 4)

            next_revision.value = int(next_revision.value/24)   #Calculation hours to days
            total_water.value =float(total_water.value/1000) # Calculating from L to mÂł

            if config_getjudo.USE_WITH_SOFTWELL_P == False:
                if holidaymode.value == 3:      #mode1
                    holidaymode.value = messages_getjudo.holiday_options[2]
                elif holidaymode.value == 5:    #mode2
                    holidaymode.value = messages_getjudo.holiday_options[3]
                elif holidaymode.value == 9:    #lock
                    holidaymode.value = messages_getjudo.holiday_options[1]
                else:                           #off
                    holidaymode.value = messages_getjudo.holiday_options[0]

                total_softwater_proportion.value = float(total_softwater_proportion.value/1000)# Calculating from L to mÂł
                total_hardwater_proportion.value = round((total_water.value - total_softwater_proportion.value),3)
                salt_stock.value /= 1000 
                if water_lock.value > 1:
                    water_lock.value = 1

            regeneration_start.value &= 0x0F
            if regeneration_start.value > 0:
                regeneration_start.value = 1


            today = date.today()
            #It's 12pm...a new day. Store today's value to yesterday's value and setting a new offset for a new count
            if today.day != mydata.day_today:
                mydata.day_today = today.day
                mydata.offset_total_water = int(1000*total_water.value)
                water_yesterday.value = water_today.value
                mydata.water_yesterday = water_today.value
            water_today.value = int(1000*total_water.value) - mydata.offset_total_water

            #Hours since last regeneration / Average regeneration interval
            if regenerations.value > mydata.reg_last_val:
                if (regenerations.value - mydata.reg_last_val) == 1: #Regeneration has started, 
                    if mydata.reg_last_timestamp != 0:
                        h_since_last_reg.value = math.ceil((int(time.time()) - mydata.reg_last_timestamp)/3600)
                        #neuer_mittelwert = ((counter-1)*alter_mittelwert + neuer_wert)/counter
                        avg_reg_interval.value = math.ceil(((mydata.reg_mean_counter-1)*mydata.reg_mean_time + h_since_last_reg.value)/mydata.reg_mean_counter)
                        mydata.reg_mean_time = avg_reg_interval.value
                        mydata.reg_mean_counter += 1
                    mydata.reg_last_timestamp = int(time.time()) 
                    mydata.reg_last_val = regenerations.value
                    if config_getjudo.USE_WITH_SOFTWELL_P == False:
                        mydata.total_softwater_at_reg = total_softwater_proportion.value
                        mydata.total_hardwater_at_reg = total_hardwater_proportion.value
                else:
                    mydata.reg_last_val = regenerations.value
            if mydata.reg_last_timestamp != 0:
                h_since_last_reg.value = int((int(time.time()) - mydata.reg_last_timestamp)/3600)

            #Mix ratio Soft:Hard since last regeneration
            if config_getjudo.USE_WITH_SOFTWELL_P == False:
                softwater_since_reg = total_softwater_proportion.value - mydata.total_softwater_at_reg
                hardwater_since_reg = total_hardwater_proportion.value - mydata.total_hardwater_at_reg
                if softwater_since_reg != 0 and hardwater_since_reg !=0:
                    totalwater_since_reg = softwater_since_reg +  hardwater_since_reg

                    if hardwater_since_reg < softwater_since_reg:
                        mixratio.value = "1:" + str(round(1/(hardwater_since_reg/totalwater_since_reg),2))
                    else:
                        mixratio.value = str(round(1/(softwater_since_reg/totalwater_since_reg),2)) + ":1"
                else:
                    mixratio.value = "unknown"


            #print("Publishing parsed values over MQTT....")
            outp_val_dict = {}
            for obj in gc.get_objects():
                if isinstance(obj, entity):
                    outp_val_dict[obj.name] = str(obj.value)
            publish_json(client, state_topic, outp_val_dict)

        elif response_json["status"] == "error":
            notify.counter += 1
            if response_json["data"] == "login failed":
                notify.publish(messages_getjudo.debug[23],3)
                mydata.token = judo_login(config_getjudo.JUDO_USER, config_getjudo.JUDO_PASSWORD)
            else:
                val = response_json["data"]
                notify.publish(messages_getjudo.debug[24].format(val),3)
                notify.counter += 1
        else:
            print(messages_getjudo.debug[25])
            notify.counter += 1
    except Exception as e:
        notify.publish([messages_getjudo.debug[31].format(sys.exc_info()[-1].tb_lineno),e],3)
        notify.counter += 1

    try:
        error_response = http.request('GET',f"https://myjudo.eu/interface/?token={mydata.token}&group=register&command=get%20error%20messages")
        error_response_json = json.loads(error_response.data)
        if error_response_json["data"] != [] and error_response_json["count"] != 0:
            if mydata.last_err_id != error_response_json["data"][0]["id"]:
                mydata.last_err_id = error_response_json["data"][0]["id"]

                timestamp = error_response_json["data"][0]["ts_sort"]
                timestamp = timestamp[:-7] + ": "

                if error_response_json["data"][0]["type"] == "w":
                    error_message = timestamp + messages_getjudo.warnings[error_response_json["data"][0]["error"]]
                    notify.publish(error_message, 1)
                elif error_response_json["data"][0]["type"] == "e":
                    error_message = timestamp + messages_getjudo.errors[error_response_json["data"][0]["error"]]
                    notify.publish(error_message, 1)
    except Exception as e:
        notify.publish([messages_getjudo.debug[30].format(sys.exc_info()[-1].tb_lineno),e], 3)
        notify.counter += 1

    try:
        with open(config_getjudo.TEMP_FILE,"wb") as temp_file:
            pickle.dump(mydata, temp_file)
    except Exception as e:
        notify.publish([messages_getjudo.debug[29].format(sys.exc_info()[-1].tb_lineno),e], 3)
        notify.counter += 1

    if notify.counter >= config_getjudo.MAX_RETRIES:
        notify.publish(messages_getjudo.debug[32].format(config_getjudo.MAX_RETRIES),1)
        sys.exit()
    else:
        notify.counter = 0
#---------------------

Function_Caller(config_getjudo.STATE_UPDATE_INTERVAL, main).start()

notify.publish(messages_getjudo.debug[39], 2)   #Init Complete


thanks @threadstone83

2 Likes

Am I the only one who has problems with this entity?
sensor.judo_isoft_xxx_mischungsverhaeltnis_weich_hart

Is shown as unknown and HA shows the following in the log:


* Exception raised while updating state of sensor.judo_isoft_xxx_judo_isoft_xxx_mischungsverhaeltnis_weich_hart, topic: 'xxx/Judo_isoft/state' with payload: b'{"Revision_in": "0", "Gesamtwasserverbrauch": "534.673", "Wunschwasserhaerte": "5", "Rohwasserhaerte": "23", "Anzahl_Regenerationen": "1284", "Regeneration": "1", "Verbrauch_Heute": "139", "Verbrauch_Gestern": "236", "Stunden_seit_letzter_Regeneration": "31", "durchschn_Regenerationsintervall": "24", "Salzvorrat": "14.625", "Salzreichweite": "67", "Gesamtweichwasseranteil": "346.498", "Gesamthartwasseranteil": "188.175", "Wasserdurchflussmenge": "161", "Batterierestkapazitaet": "0", "Wasser_absperren": "0", "Sleepmode": "0", "max_Entnahmedauer": "30", "max_Wasserdurchfluss": "2500", "max_Entnahmemenge": "300", "Urlaubsmodus": "Aus", "Mischungsverhaeltnis_Weich_Hart": "1:3.62"}'

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 640, in state
    numerical_value = float(value)  # type:ignore[arg-type]
ValueError: could not convert string to float: '1:3.82'