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'

Just wanted to let you know that I successfully installed your integration today. Happy to do some testing if required. I might raise some feature tickets e.g. for having the “Salz nachfüllen” message somewhere.
Overall a really promising implementation and I really like the fact you’re making use of the local API opposed to the public internet service.
Appreciate your efforts on this integration and will certainly help if possible.
Thanks much

Yes, salt refill will be the next topic ;-). I’d like to implement it as follows:

  • Pressing button “Salt refill”
  • Popup dialog opens to ask for the amount: fix entries 5, 10, 25 kg or arbitrary value

However, I did not find a way to build a popup window in Home Assistant up to now.

Has someone any ideas how to solve this?

not sure for integrations. for some custom popups I use browser mod. but I guess this will not help you