Não manjo de NR, a primeira vez que mexi foi agora para fazer esse fluxo. Eu tentei integrar do NR direto para o HA, perdi um tempão e não consegui, só foi via MQTT. Não sei se essa foi sua pergunta…
Vc instalou o addon e a integração do node red?
A integração é necessária para que os sensores apareceram no HA.
Posta o fluxo depois, se eu tiver um tempo crio os sensores
Sinceramente @henderson e @felipe.villa não usaria o node-red pra essa comunicação TCP, não é muito confiavel e resiliente já que teoricamente só da pra conectar 1x, se eu fosse vocês eu usuaria algo mais customizado.
Mas caso queiram fazer, colocaria pelo menos um try-catch, retry e timeout nessa abertura de conexão.
Realmente, nodered não é ideal, longe disso±. Mas é uma solução rápida.
Fiz hoje em algumas horas o que eu precisava para ter minimamente o controle da piscina no HA.
Não é confiável deixar a conexão aberta no nodered, então preciso ficar abrindo/fechando toda vez que vou mandar um comando ou consultar os status. Usei um node de TCP customizado, não o padrão que vem no nodered, porque esse me dá mais flexibilidade, com retry, delays etc.
Também é ruim manter os botões de liga/desliga do HA sincronizados com o que de fato está no Tholz, já que além do HA, o Tholz é comandado pela Alexa e pelo controle remoto.
Cheio de gambiarra rs. Mas funciona minimamente bem.
Com mais tempo, penso em fazer uma integração mais robusta.
Caso alguém queira meu fluxo complicado e bagunçado rs…
No HA precisa criar os input_boolean para cada comando (poderia ser no nodered, mas prefiro no HA). Os sensores fiz a criação da entidade direto no nodered.
Ficou assim o controle
[{"id":"fbe3de3d88fb5cbe","type":"tab","label":"Tholz","disabled":false,"info":"","env":[]},{"id":"fb7171e844cf99c4","type":"tcp-client","z":"fbe3de3d88fb5cbe","action":"action","actionType":"msg","host":"192.168.1.43","hostType":"str","port":"4000","portType":"str","datamode":"single","datatype":"utf8","newline":"\\n","write":"payload","writeType":"msg","maxRetries":"5","retryDelay":"3000","indefiniteRetries":false,"topic":"","name":"TCP Tholz","debug":"error","x":610,"y":240,"wires":[["bc2d54049fd97fe2","3ed18a6e438ba018"]]},{"id":"4dbc7508f937ac9e","type":"change","z":"fbe3de3d88fb5cbe","name":"Setar Valores Open","rules":[{"t":"set","p":"action","pt":"msg","to":"connect","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":240,"wires":[["fb7171e844cf99c4","f28ccc23868e64b9"]]},{"id":"57d60a9fb3900b93","type":"inject","z":"fbe3de3d88fb5cbe","name":"Start Manual","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":300,"wires":[["4dbc7508f937ac9e"]]},{"id":"f28ccc23868e64b9","type":"change","z":"fbe3de3d88fb5cbe","name":"Setar Valores Consulta","rules":[{"t":"set","p":"action","pt":"msg","to":"write","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"{\"command\": \"getDevice\"}","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":320,"wires":[["fb7171e844cf99c4"]]},{"id":"a5d8db9caed30cff","type":"function","z":"fbe3de3d88fb5cbe","name":"Processamento da resposta","func":"var erroCode = msg.payload.response?.error ?? null; // Captura o código do erro (int)\nvar erro = `Erro desconhecido \"${erroCode}\"`; // Mensagem padrão se não houver correspondência\n\nconst text = {\n 0: ['Sem erro ou aviso.', 'Nenhuma descrição.'],\n 1: ['Sobreaquecimento.', 'O sensor de temperatura do coletor solar identificou um valor elevado. Para evitar qualquer dano à tubulação, a circulação de água será evitada.'],\n 2: ['Anticongelamento.', 'O sensor de temperatura do coletor solar identificou um valor muito baixo. Para evitar qualquer dano à tubulação, a circulação de água será ativada.'],\n 3: ['Falta de Água.', 'O sensor de nível do dispositivo identificou que não há água na banheira. Por segurança, nenhuma hidromassagem pode ser acionada. Verifique se o equipamento está calibrado.'],\n 4: ['Resfriamento da Resistência.', 'A hidromassagem permanecerá ligada por mais alguns segundos, até que a resistência esteja resfriada. Não desligue a saída durante esse período.'],\n 5: ['Erro de temperatura.', 'O sensor de temperatura do dispositivo identificou uma leitura inadequada. Verifique o local do sensor e sua instalação.'],\n 6: ['Termostato Desarmado.', 'O termostato identificou um sobreaquecimento. Para evitar qualquer dano ao sistema, o aquecimento da água foi desativado.'],\n 7: ['PARÂMETRO LEGADO.', 'ESPAÇO DISPONÍVEL PARA PRODUTOS DA VERSÃO MODBUS 1.0.'],\n 8: ['Pressão Alta.', 'O calor do sistema não está sendo absorvido pela água, gerando superaquecimento. Verifique a vazão de água do sistema.'],\n 9: ['Pressão Baixa.', 'Foi identificada uma baixa pressão no gás refrigerante. Isso pode ocorrer após o primeiro acionamento da bomba ou por uma baixa temperatura ambiente.'],\n 10: ['Baixo fluxo de água.', 'Verifique a limpeza do sistema filtrante, a bomba e a posição dos registros de água do equipamento.'],\n 11: ['Ciclo de degelo.', 'O ciclo de degelo foi ativado devido à identificação de baixa temperatura ambiente.'],\n 12: ['Erro de Alimentação.', 'A alimentação do trocador se encontra fora da faixa de operação.'],\n 13: ['Parâmetro Inconsistente.', 'A configuração dos parâmetros do seu controlador apresenta uma inconsistência quanto ao setpoint. Ajuste o parâmetro de setpoint máximo do seu produto.'],\n 14: ['Erro sensor 1.', 'O sensor de temperatura T1 do dispositivo identificou uma leitura inadequada. Verifique o local do sensor e sua instalação.'],\n 15: ['Erro sensor 2.', 'O sensor de temperatura T2 do dispositivo identificou uma leitura inadequada. Verifique o local do sensor e sua instalação.'],\n 16: ['Erro sensor 3.', 'O sensor de temperatura T3 do dispositivo identificou uma leitura inadequada. Verifique o local do sensor e sua instalação.'],\n 17: ['Erro sensor 4.', 'O sensor de temperatura T4 do dispositivo identificou uma leitura inadequada. Verifique o local do sensor e sua instalação.'],\n 18: ['Erro sensor 5.', 'O sensor de temperatura T5 do dispositivo identificou uma leitura inadequada. Verifique o local do sensor e sua instalação.'],\n 19: ['Erro sensor 6.', 'O sensor de temperatura T6 do dispositivo identificou uma leitura inadequada. Verifique o local do sensor e sua instalação.'],\n 20: ['Dados inconsistentes.', 'O produto apresentou um comportamento inesperado. Entre em contato com o suporte.']\n};\n\n// Determina os valores a serem usados\nconst result = text[erroCode] ?? [erro, ''];\n\n// Inicializa msg.response caso não exista\nmsg.response = msg.response || {};\nmsg.response.error = result[0];\nmsg.response.error_description = result[1];\n\n//processar led\nif (msg.payload.response?.leds) {\n msg.response.leds = {};\n //Itera sobre cada led e processa\n\n for (let key in msg.payload.response.leds) {\n let led = msg.payload.response.leds[key];\n\n //verifica se o led não é null\n if (led != null) {\n let ledType = '';\n switch (led.type) {\n case 0:\n ledType = 'Mono';\n break;\n\n case 1:\n ledType = 'RGB';\n break;\n case 2:\n ledType = 'RGBW';\n default:\n ledType = 'Desconhecido';\n }\n\n // Adiciona as informações no objeto msg.response.heatings\n msg.response.leds[key] = {\n type: ledType,\n on: led.on,\n onAut: led.onAut,\n effect: led.effect,\n brightness: led.brightness,\n speed: led.speed,\n color: [led.color[0], led.color[1], led.color[2]]\n };\n\n }\n }\n}\n\n// Processa os aquecimentos da resposta\nif (msg.payload.response?.heatings) {\n msg.response.heatings = {};\n\n // Itera sobre cada aquecimento e processa\n for (let key in msg.payload.response.heatings) {\n let heating = msg.payload.response.heatings[key];\n \n // Verifica se o aquecimento não é null\n if (heating !== null) {\n let heatingType = '';\n \n // Determina o tipo de aquecimento\n switch (heating.type) {\n case 0:\n heatingType = 'Solar Piscina';\n break;\n case 1:\n heatingType = 'Trocador de Calor Piscina';\n break;\n case 2:\n heatingType = 'Elétrico Piscina';\n break;\n case 3:\n heatingType = 'Solar Residencial';\n break;\n case 4:\n heatingType = 'Apoio a Gás';\n break;\n case 5:\n heatingType = 'Apoio Elétrico';\n break;\n case 6:\n heatingType = 'Recirculação de Barrilete';\n break;\n case 7:\n heatingType = 'Trocador de Calor Fairland';\n break;\n case 8:\n heatingType = 'Aquecimento';\n break;\n case 9:\n heatingType = 'Refrigeração';\n break;\n case 10:\n heatingType = 'Termostato';\n break;\n default:\n heatingType = 'Desconhecido';\n break;\n }\n\n let opMode = '';\n // Determina o modo de operação\n switch (heating.opMode) {\n case 0:\n opMode = 'Desligado';\n break;\n case 1:\n opMode = 'Ligado';\n break;\n case 2:\n opMode = 'Automático';\n break;\n case 3:\n opMode = 'Econômico';\n break;\n case 4:\n opMode = 'Aquecer';\n break;\n case 5:\n opMode = 'Resfriar';\n break;\n default:\n opMode = 'Desconhecido';\n break;\n }\n\n // Converte step, sp, minSp e maxSp para float\n const step = heating.step / 10;\n const minSp = heating.minSp / 10;\n const maxSp = heating.maxSp / 10;\n const sp = heating.sp / 10;\n\n // Adiciona as informações no objeto msg.response.heatings\n msg.response.heatings[key] = {\n type: heatingType,\n step: step,\n minSp: minSp,\n maxSp: maxSp,\n sp: sp,\n on: heating.on,\n onAut: heating.onAut,\n aux: heating.aux,\n opMode: opMode\n };\n\n // Processa os sensores t1, t2, t3, t4, etc. e os adiciona diretamente no aquecimento\n let sensorIndex = 1;\n \n // Para cada sensor, processa e converte para float\n while (heating[`t${sensorIndex}`] !== undefined) {\n msg.response.heatings[key][`t${sensorIndex}`] = heating[`t${sensorIndex}`] / 10;\n sensorIndex++;\n }\n }\n }\n}\n\n// Processa os outputs da resposta\nif (msg.payload.response?.outputs) {\n msg.response.outputs = {};\n\n // Itera sobre cada saída e processa\n for (let key in msg.payload.response.outputs) {\n let output = msg.payload.response.outputs[key];\n\n // Verifica se a saída não é null\n if (output !== null) {\n let outputType = '';\n\n // Determina o tipo de saída\n switch (output.id) {\n case 0:\n outputType = 'Filtro';\n break;\n case 1:\n outputType = 'Apoio Elétrico';\n break;\n case 2:\n outputType = 'Apoio a Gás';\n break;\n case 3:\n outputType = 'Recirculação';\n break;\n case 4:\n outputType = 'Borbulhador';\n break;\n case 5:\n outputType = 'Circulação';\n break;\n case 10:\n case 11:\n case 12:\n case 13:\n case 14:\n case 15:\n case 16:\n case 17:\n case 18:\n case 19:\n outputType = 'Auxiliares';\n break;\n case 20:\n case 21:\n case 22:\n case 23:\n case 24:\n case 25:\n case 26:\n case 27:\n case 28:\n case 29:\n outputType = 'Cascatas';\n break;\n case 30:\n case 31:\n case 32:\n case 33:\n case 34:\n case 35:\n case 36:\n case 37:\n case 38:\n case 39:\n outputType = 'Hidros';\n break;\n case 40:\n case 41:\n case 42:\n case 43:\n case 44:\n case 45:\n case 46:\n case 47:\n case 48:\n case 49:\n case 50:\n case 51:\n case 52:\n case 53:\n case 54:\n case 55:\n outputType = 'Interruptores';\n break;\n default:\n outputType = 'Desconhecido';\n break;\n }\n\n // Adiciona as informações no objeto msg.response.outputs\n msg.response.outputs[key] = {\n type: outputType,\n on: output.on,\n onAut: output.onAut\n };\n }\n }\n}\n\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":880,"y":240,"wires":[["d8eeb2a41d8d663a"]]},{"id":"bc2d54049fd97fe2","type":"change","z":"fbe3de3d88fb5cbe","name":"Setar Valores Close","rules":[{"t":"set","p":"action","pt":"msg","to":"close","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":400,"wires":[["fb7171e844cf99c4"]]},{"id":"3ed18a6e438ba018","type":"json","z":"fbe3de3d88fb5cbe","name":"","property":"payload","action":"obj","pretty":false,"x":750,"y":300,"wires":[["a5d8db9caed30cff"]]},{"id":"52dab77181d2eb30","type":"ha-sensor","z":"fbe3de3d88fb5cbe","name":"Piscina Temperatura","entityConfig":"f054b7d7bd0de733","version":0,"state":"response.heatings.heat0.t2","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1380,"y":80,"wires":[[]]},{"id":"a623069a6ecf25e8","type":"change","z":"fbe3de3d88fb5cbe","name":"Setar Valores Open","rules":[{"t":"set","p":"action","pt":"msg","to":"connect","tot":"str"},{"t":"set","p":"comando","pt":"msg","to":"comando","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":740,"wires":[["2ebbc10abb759951","ded4f4a10a282943"]]},{"id":"ded4f4a10a282943","type":"change","z":"fbe3de3d88fb5cbe","name":"Setar Valores Comando","rules":[{"t":"set","p":"action","pt":"msg","to":"write","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"comando","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":810,"y":680,"wires":[["2ebbc10abb759951"]]},{"id":"2ebbc10abb759951","type":"tcp-client","z":"fbe3de3d88fb5cbe","action":"action","actionType":"msg","host":"192.168.1.43","hostType":"str","port":"4000","portType":"str","datamode":"single","datatype":"utf8","newline":"\\n","write":"payload","writeType":"msg","maxRetries":"5","retryDelay":"3000","indefiniteRetries":false,"topic":"","name":"TCP Tholz","debug":"error","x":1070,"y":720,"wires":[["a74825c754268c0f"]]},{"id":"a74825c754268c0f","type":"change","z":"fbe3de3d88fb5cbe","name":"Setar Valores Close","rules":[{"t":"set","p":"action","pt":"msg","to":"close","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1070,"y":800,"wires":[["2ebbc10abb759951"]]},{"id":"e12542816bf1ef5d","type":"server-state-changed","z":"fbe3de3d88fb5cbe","name":"Piscina Comando Filtro","server":"5b3a2980.afffa8","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["input_boolean.piscina_comando_filtro"],"substring":[],"regex":[]},"outputInitially":false,"stateType":"habool","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"},{"property":"output","propertyType":"msg","value":"0","valueType":"str"}],"x":140,"y":680,"wires":[["e40285081fd2c74f"]]},{"id":"e0bc4d2e6add9157","type":"server-state-changed","z":"fbe3de3d88fb5cbe","name":"Piscina Comando Cascata","server":"5b3a2980.afffa8","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["input_boolean.piscina_comando_cascata"],"substring":[],"regex":[]},"outputInitially":false,"stateType":"habool","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"},{"property":"output","propertyType":"msg","value":"1","valueType":"str"}],"x":130,"y":740,"wires":[["e40285081fd2c74f"]]},{"id":"2314c20b63b69db2","type":"server-state-changed","z":"fbe3de3d88fb5cbe","name":"Piscina Comando Hidro","server":"5b3a2980.afffa8","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["input_boolean.piscina_comando_hidro"],"substring":[],"regex":[]},"outputInitially":false,"stateType":"habool","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"},{"property":"output","propertyType":"msg","value":"2","valueType":"str"}],"x":140,"y":800,"wires":[["e40285081fd2c74f"]]},{"id":"e40285081fd2c74f","type":"function","z":"fbe3de3d88fb5cbe","name":"montar comando output","func":"let out = \"out\" + msg.output;\n\nlet comando = {\n command : 'setDevice',\n argument: {\n outputs: {\n [\"out\" + msg.output]: {\n on: msg.payload\n }\n }\n }\n};\n\nmsg.comando = comando;\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":740,"wires":[["18b48dec71b9dd35"]]},{"id":"c36f3b3d53e80808","type":"ha-binary-sensor","z":"fbe3de3d88fb5cbe","name":"Piscina Aquecedor","entityConfig":"1d1cb2e71a51da08","version":0,"state":"response.heatings.heat0.on","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1370,"y":140,"wires":[[]]},{"id":"e79df90db05b87bc","type":"server-state-changed","z":"fbe3de3d88fb5cbe","name":"Piscina Comando LED","server":"5b3a2980.afffa8","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["input_boolean.piscina_led"],"substring":[],"regex":[]},"outputInitially":false,"stateType":"habool","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"},{"property":"output","propertyType":"msg","value":"0","valueType":"str"}],"x":140,"y":860,"wires":[["afdcb9fcdf0faa5d"]]},{"id":"afdcb9fcdf0faa5d","type":"function","z":"fbe3de3d88fb5cbe","name":"montar comando led","func":"let comando = {\n command : 'setDevice',\n argument: {\n leds: {\n led0: {\n on: msg.payload\n }\n }\n }\n};\n\nmsg.comando = comando;\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":420,"y":860,"wires":[["18b48dec71b9dd35"]]},{"id":"4eba1dee4fbdbc08","type":"server-state-changed","z":"fbe3de3d88fb5cbe","name":"Piscina Atualizar","server":"5b3a2980.afffa8","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["input_button.piscina_atualizar"],"substring":[],"regex":[]},"outputInitially":false,"stateType":"habool","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":false,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"},{"property":"output","propertyType":"msg","value":"0","valueType":"str"}],"x":120,"y":180,"wires":[["4dbc7508f937ac9e"]]},{"id":"e21df18e91e22b43","type":"server-state-changed","z":"fbe3de3d88fb5cbe","name":"Comando de timer manual","server":"2620ae25.44eb12","version":6,"outputs":2,"exposeAsEntityConfig":"","entities":{"entity":["input_boolean.bomba_piscina_timer_manual_comando"],"substring":[],"regex":[]},"outputInitially":false,"stateType":"habool","ifState":"true","ifStateType":"bool","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":150,"y":960,"wires":[["f7b1745982c4f6b3"],[]]},{"id":"f7b1745982c4f6b3","type":"api-current-state","z":"fbe3de3d88fb5cbe","name":"Obter tempo do timer","server":"2620ae25.44eb12","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_number.bomba_piscina_timer_manual_tempo","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":0,"forType":"num","forUnits":"minutes","x":400,"y":960,"wires":[["6090bcdcc3a8e325"]]},{"id":"6090bcdcc3a8e325","type":"function","z":"fbe3de3d88fb5cbe","name":"Configurar tempo do timer","func":"/*if (msg.topic === \"delay\") {\n context.set(\"delay\", msg.payload);\n msg = null;\n} else {\n delay = context.get(\"delay\");\n if (delay === undefined) delay = 1000; // default delay ms\n msg.delay = delay;\n}\nreturn msg;\n*/\n//msg.payload = 'timer'\n\n//Delay to be passed em ms, but received in minutes\n//Convert -> *60 = minutes | *1000 = seconds\nmsg.delay = msg.data.state*60*1000;\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":1020,"wires":[["06993dbae23a7618"]]},{"id":"06993dbae23a7618","type":"delay","z":"fbe3de3d88fb5cbe","name":"Aguardar timer","pauseType":"delayv","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":540,"y":1080,"wires":[["d8784ea35867cab1"]]},{"id":"075d73562292017a","type":"api-call-service","z":"fbe3de3d88fb5cbe","name":"Desligar bomba","server":"5b3a2980.afffa8","version":7,"debugenabled":false,"action":"switch.turn_off","floorId":[],"areaId":[],"deviceId":["677e49c6eb894f7d73c92136ad5fd602"],"entityId":[],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":false,"domain":"switch","service":"turn_off","x":760,"y":1080,"wires":[[]]},{"id":"34beda823928bf83","type":"api-call-service","z":"fbe3de3d88fb5cbe","name":"Notificar HA Hend","server":"5b3a2980.afffa8","version":7,"debugenabled":false,"action":"notify.mobile_app_note10hend","floorId":[],"areaId":[],"deviceId":[],"entityId":[],"labelId":[],"data":"{\"message\":\"Bomba desligada pelo timer manual\",\"title\":\"Bomba piscina\",\"data\":{\"ttl\":0,\"priority\":\"high\"}}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":false,"domain":"notify","service":"mobile_app_note10hend","x":970,"y":1020,"wires":[[]]},{"id":"d941f81d057d4dec","type":"api-call-service","z":"fbe3de3d88fb5cbe","name":"Desligar Botão Timer","server":"5b3a2980.afffa8","version":7,"debugenabled":false,"action":"input_boolean.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.bomba_piscina_timer_manual_comando"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":false,"domain":"input_boolean","service":"turn_off","x":820,"y":1160,"wires":[[]]},{"id":"06d3beba7a7612fd","type":"change","z":"fbe3de3d88fb5cbe","name":"Setar Valores Open","rules":[{"t":"set","p":"action","pt":"msg","to":"connect","tot":"str"},{"t":"set","p":"comando","pt":"msg","to":"comando","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":710,"y":1320,"wires":[["d31de799d8a3290c","ced65d05d5ac81c5"]]},{"id":"ced65d05d5ac81c5","type":"change","z":"fbe3de3d88fb5cbe","name":"Setar Valores Comando","rules":[{"t":"set","p":"action","pt":"msg","to":"write","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"comando","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":730,"y":1260,"wires":[["d31de799d8a3290c"]]},{"id":"d31de799d8a3290c","type":"tcp-client","z":"fbe3de3d88fb5cbe","action":"action","actionType":"msg","host":"192.168.1.43","hostType":"str","port":"4000","portType":"str","datamode":"single","datatype":"utf8","newline":"\\n","write":"payload","writeType":"msg","maxRetries":"5","retryDelay":"3000","indefiniteRetries":false,"topic":"","name":"TCP Tholz","debug":"error","x":970,"y":1300,"wires":[["e53e4423a507c91d","d941f81d057d4dec","34beda823928bf83"]]},{"id":"e53e4423a507c91d","type":"change","z":"fbe3de3d88fb5cbe","name":"Setar Valores Close","rules":[{"t":"set","p":"action","pt":"msg","to":"close","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":970,"y":1380,"wires":[["d31de799d8a3290c"]]},{"id":"d8784ea35867cab1","type":"function","z":"fbe3de3d88fb5cbe","name":"montar comando output","func":"let comando = {\n command : 'setDevice',\n argument: {\n outputs: {\n out0: {\n on: false\n }\n }\n }\n};\n\nmsg.comando = comando;\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":690,"y":1380,"wires":[["06d3beba7a7612fd"]]},{"id":"f87669f108051632","type":"comment","z":"fbe3de3d88fb5cbe","name":"Timer do dashboard para desligar filtro","info":"","x":190,"y":900,"wires":[]},{"id":"690ed43a289e96a9","type":"comment","z":"fbe3de3d88fb5cbe","name":"Executa comandos no Tholz","info":"","x":160,"y":620,"wires":[]},{"id":"4e25b8afc1848603","type":"comment","z":"fbe3de3d88fb5cbe","name":"Atualiza status dos botões com dados do Tholz","info":"","x":220,"y":120,"wires":[]},{"id":"36ebc35358133dcb","type":"comment","z":"fbe3de3d88fb5cbe","name":"Inicializar variável de controle","info":"","x":160,"y":40,"wires":[]},{"id":"b40090b845039a77","type":"change","z":"fbe3de3d88fb5cbe","name":"Inicializar var de status","rules":[{"t":"set","p":"atualizando_status","pt":"flow","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":80,"wires":[[]]},{"id":"2893564f4c2fd737","type":"inject","z":"fbe3de3d88fb5cbe","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":80,"wires":[["b40090b845039a77"]]},{"id":"18b48dec71b9dd35","type":"switch","z":"fbe3de3d88fb5cbe","name":"atualizando?","property":"atualizando_status","propertyType":"flow","rules":[{"t":"eq","v":"0","vt":"num"}],"checkall":"true","repair":true,"outputs":1,"x":610,"y":800,"wires":[["a623069a6ecf25e8"]]},{"id":"8a0de8bb42521c08","type":"change","z":"fbe3de3d88fb5cbe","name":"Atualizar var de status","rules":[{"t":"set","p":"atualizando_status","pt":"flow","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":540,"wires":[[]]},{"id":"5f83216a8b61cd62","type":"complete","z":"fbe3de3d88fb5cbe","name":"Encerrar Atualização","scope":["c36f3b3d53e80808","f7c8114a357bb898","a864ec1c987866f1"],"uncaught":false,"x":130,"y":540,"wires":[["8a0de8bb42521c08"]]},{"id":"80e798c205b43ffb","type":"comment","z":"fbe3de3d88fb5cbe","name":"Controle de atualização de status","info":"Como o HA usa input_boolean, seus status ficam desatualizados com o real status no Tholz.\n\nPara \"sincronizar\" esse status, obtemos os dados do Tholz e atualizamos o status dos input_boolean no HA\nIsso dispararia os triggers que enviam os comandos liga/desliga ao Tholz, por isso a variável atualizando_status é utilizada para indicar no fluxo que a última mudança de status do input_boolean foi feita pelo nodered e não deve ser enviada ao Tholz.\n\nEsse fluxo irá rodar quando todos os input_boolean forem atualizados","x":170,"y":500,"wires":[]},{"id":"d8eeb2a41d8d663a","type":"change","z":"fbe3de3d88fb5cbe","name":"Atualizar var de status","rules":[{"t":"set","p":"atualizando_status","pt":"flow","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1140,"y":240,"wires":[["52dab77181d2eb30","c36f3b3d53e80808","918386e767ca5281","a9b51838ad71d1cc","7a27093705882e5c","a4d0209d30c5f222"]]},{"id":"918386e767ca5281","type":"switch","z":"fbe3de3d88fb5cbe","name":"filtro ligado?","property":"response.outputs.out0.on","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":1350,"y":240,"wires":[["f7c8114a357bb898"],["a864ec1c987866f1"]]},{"id":"f7c8114a357bb898","type":"api-call-service","z":"fbe3de3d88fb5cbe","name":"Filtro Ligado","server":"5b3a2980.afffa8","version":7,"debugenabled":false,"action":"input_boolean.turn_on","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.piscina_comando_filtro"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"input_boolean","service":"turn_on","x":1590,"y":200,"wires":[[]]},{"id":"a864ec1c987866f1","type":"api-call-service","z":"fbe3de3d88fb5cbe","name":"Filtro Desigado","server":"5b3a2980.afffa8","version":7,"debugenabled":false,"action":"input_boolean.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.piscina_comando_filtro"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"input_boolean","service":"turn_off","x":1600,"y":260,"wires":[[]]},{"id":"a9b51838ad71d1cc","type":"switch","z":"fbe3de3d88fb5cbe","name":"cascata ligada?","property":"response.outputs.out1.on","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":1380,"y":360,"wires":[["91f16ec28c13a045"],["101619dae86c6c01"]]},{"id":"91f16ec28c13a045","type":"api-call-service","z":"fbe3de3d88fb5cbe","name":"Cascata Ligada","server":"5b3a2980.afffa8","version":7,"debugenabled":false,"action":"input_boolean.turn_on","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.piscina_comando_cascata"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"input_boolean","service":"turn_on","x":1600,"y":320,"wires":[[]]},{"id":"101619dae86c6c01","type":"api-call-service","z":"fbe3de3d88fb5cbe","name":"Cascata Desligada","server":"5b3a2980.afffa8","version":7,"debugenabled":false,"action":"input_boolean.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.piscina_comando_cascata"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"input_boolean","service":"turn_off","x":1610,"y":380,"wires":[[]]},{"id":"7a27093705882e5c","type":"switch","z":"fbe3de3d88fb5cbe","name":"hidro ligada?","property":"response.outputs.out2.on","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":1370,"y":460,"wires":[["af3ad7fd62a85889"],["a548d28a4d8f4b53"]]},{"id":"af3ad7fd62a85889","type":"api-call-service","z":"fbe3de3d88fb5cbe","name":"Hidro Ligada","server":"5b3a2980.afffa8","version":7,"debugenabled":false,"action":"input_boolean.turn_on","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.piscina_comando_hidro"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"input_boolean","service":"turn_on","x":1610,"y":440,"wires":[[]]},{"id":"a548d28a4d8f4b53","type":"api-call-service","z":"fbe3de3d88fb5cbe","name":"Hidro Desligada","server":"5b3a2980.afffa8","version":7,"debugenabled":false,"action":"input_boolean.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.piscina_comando_hidro"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"input_boolean","service":"turn_off","x":1620,"y":480,"wires":[[]]},{"id":"a4d0209d30c5f222","type":"switch","z":"fbe3de3d88fb5cbe","name":"led ligado?","property":"response.leds.led0.on","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":1370,"y":560,"wires":[["c73a3c3adae2dfc1"],["eab5e0890c3396d1"]]},{"id":"c73a3c3adae2dfc1","type":"api-call-service","z":"fbe3de3d88fb5cbe","name":"LED Ligado","server":"5b3a2980.afffa8","version":7,"debugenabled":false,"action":"input_boolean.turn_on","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.piscina_led"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"input_boolean","service":"turn_on","x":1610,"y":540,"wires":[[]]},{"id":"eab5e0890c3396d1","type":"api-call-service","z":"fbe3de3d88fb5cbe","name":"LED Desligado","server":"5b3a2980.afffa8","version":7,"debugenabled":false,"action":"input_boolean.turn_off","floorId":[],"areaId":[],"deviceId":[],"entityId":["input_boolean.piscina_led"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"input_boolean","service":"turn_off","x":1620,"y":580,"wires":[[]]},{"id":"5be8318af3b98dca","type":"inject","z":"fbe3de3d88fb5cbe","name":"Start Automático","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"600","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":150,"y":240,"wires":[["4dbc7508f937ac9e"]]},{"id":"cd4f4f3bfa9cd960","type":"comment","z":"fbe3de3d88fb5cbe","name":"Documentação Tholz","info":"https://delirious-windshield-a93.notion.site/Comando-de-Escrita-d306bc47400642d1ab64550c2de8928d","x":420,"y":40,"wires":[]},{"id":"f054b7d7bd0de733","type":"ha-entity-config","server":"5b3a2980.afffa8","deviceConfig":"","name":"Piscina Temperatura","version":6,"entityType":"sensor","haConfig":[{"property":"name","value":"Piscina Temperatura"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":"temperature"},{"property":"unit_of_measurement","value":"°C"},{"property":"state_class","value":"measurement"}],"resend":true,"debugEnabled":false},{"id":"5b3a2980.afffa8","type":"server","name":"Home Assistant","addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":true,"cacheJson":false,"heartbeat":false,"heartbeatInterval":"","statusSeparator":"","enableGlobalContextStore":false},{"id":"1d1cb2e71a51da08","type":"ha-entity-config","server":"5b3a2980.afffa8","deviceConfig":"","name":"Piscina Aquecedor","version":6,"entityType":"binary_sensor","haConfig":[{"property":"name","value":"Piscina Aquecedor"},{"property":"icon","value":""},{"property":"entity_picture","value":""},{"property":"entity_category","value":""},{"property":"device_class","value":"running"}],"resend":true,"debugEnabled":false},{"id":"2620ae25.44eb12","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30,"areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]
Legal que voce avancou, mas existem algumas coisas que voce poderia melhorar, o Led precisa ser uma entidade de luz RBG, assim voce consegue trocar a cor/modo também, assim como o aquecedor o tipo é diferente pra voce setar a temperatura e os modos.
Outra coisa que não lembro de ter pois isso é interno do proprio tholz é o timer, isso não vem do aparelho correto? voce fez na mão?
Enfim tem ali em cima como fiz o heating e a luz rgb, assim voce usa 100% da sua piscina.
Fiz o publish das entities via mqtt discovery, desse jeito:
def _create_light_entity(self, device_info):
"""Create MQTT entity for light."""
discovery_topic = f"{self.topic_prefix}/light/{self.device_id}_light/config"
discovery_payload = {
"name": f"{self.device_name} Light",
"state_topic": f"{self.topic_prefix}/light/{self.device_id}_light/state",
"command_topic": f"{self.topic_prefix}/light/{self.device_id}_light/set",
"brightness_state_topic": f"{self.topic_prefix}/light/{self.device_id}_light/brightness/state",
"brightness_command_topic": f"{self.topic_prefix}/light/{self.device_id}_light/brightness/set",
"rgb_state_topic": f"{self.topic_prefix}/light/{self.device_id}_light/rgb/state",
"rgb_command_topic": f"{self.topic_prefix}/light/{self.device_id}_light/rgb/set",
"effect_state_topic": f"{self.topic_prefix}/light/{self.device_id}_light/effect/state",
"effect_command_topic": f"{self.topic_prefix}/light/{self.device_id}_light/effect/set",
"state_value_template": "{{ value_json.state }}",
"brightness_value_template": "{{ value_json.brightness }}",
"rgb_value_template": "{{ value_json.rgb | join(',') }}",
"effect_value_template": "{{ value_json.effect }}",
"unique_id": f"{self.device_id}_light",
"device": {
"identifiers": [self.device_id],
"name": self.device_name,
"manufacturer": "Tholz",
"model": "Smart Pool"
},
"payload_on": "ON",
"payload_off": "OFF",
"effect_list": ["No Effect", "Static Effect", "Transition - 1", "Transition - 2", "Transition - 3", "Transition - 4", "Strobe"],
"optimistic": False,
"qos": 0,
"retain": True
}
self.client.publish(discovery_topic, payload=json.dumps(discovery_payload), retain=True)
print(f"Created MQTT entity for light: {discovery_topic}")
def _create_heating_entities(self, device_info):
"""Create MQTT entities for heating and temperature sensors."""
heating = device_info['response']['heating']
# Create climate entity for heating
heat_discovery_topic = f"{self.topic_prefix}/climate/{self.device_id}_heating/config"
heat_discovery_payload = {
"name": f"{self.device_name} Heating",
"state_topic": f"{self.topic_prefix}/climate/{self.device_id}_heating/state",
"command_topic": f"{self.topic_prefix}/climate/{self.device_id}_heating/set",
"current_temperature_topic": f"{self.topic_prefix}/climate/{self.device_id}_heating/current_temp",
"temperature_command_topic": f"{self.topic_prefix}/climate/{self.device_id}_heating/temp_set",
"mode_state_topic": f"{self.topic_prefix}/climate/{self.device_id}_heating/mode/state",
"mode_command_topic": f"{self.topic_prefix}/climate/{self.device_id}_heating/mode/set",
"unique_id": f"{self.device_id}_heating",
"device": {
"identifiers": [self.device_id],
"name": self.device_name,
"manufacturer": "Tholz",
"model": "Smart Pool"
},
"modes": ["off", "heat", "auto"], # Supported modes
"temperature_unit": "C" if heating['unit'] else "F",
"payload_on": "ON",
"payload_off": "OFF",
"state_on": "ON",
"state_off": "OFF",
"retain": True
}
self.client.publish(heat_discovery_topic, payload=json.dumps(heat_discovery_payload), retain=True)
print(f"Created MQTT entity for heating: {heat_discovery_topic}")
# Create sensor for collector temperature
heat_t1_discovery_topic = f"{self.topic_prefix}/sensor/{self.device_id}_collector_temp/config"
heat_t1_discovery_payload = {
"name": f"{self.device_name} Collector Temperature",
"state_topic": f"{self.topic_prefix}/sensor/{self.device_id}_collector_temp/state",
"unique_id": f"{self.device_id}_collector_temp",
"device": {
"identifiers": [self.device_id],
"name": self.device_name,
"manufacturer": "Tholz",
"model": "Smart Pool"
},
"unit_of_measurement": "°C" if heating['unit'] else "°F",
"device_class": "temperature",
"qos": 0,
"retain": True
}
self.client.publish(heat_t1_discovery_topic, payload=json.dumps(heat_t1_discovery_payload), retain=True)
print(f"Created MQTT sensor for collector temperature: {heat_t1_discovery_topic}")
Ah com certeza, tem muito o que melhorar nesse fluxo, mesmo usando nodered.
Fiz o que deu para deixar minimamente funcional, já que a esposa estava reclamando de “mais um aplicativo pra controlar a casa” rs
Devo tentar dar uma melhorada nele antes de partir pra uma solução mais permanente.
Obrigado pelo código, ajuda!
E sim, eu fiz um timer manual. O aplicativo do Tholz tem um timer, mas além dele depender da internet estar funcionando, ele é limitado. Você precisa fazer uma agenda com o horário de ligar/desligar. No aplicativo do Tholz eu uso o timer que liga/desliga sempre na mesma hora. Mas eu gosto de ter um timer manual para o filtro, para eu usar de forma pontual. Isso é pra quando eu jogo produto na piscina, por exemplo, ligar o filtro e indicar quanto tempo quero que fique ligado. Quando bater o tempo, desliga e encerra aquele timer.
Que legal. Pena que nao tenho skills para fazer. Será que com essa API, seria possivel uma integração?
Ou alguem criar um tutorial de como configurar isso?
Oi pessoal! Tudo bem?
Quero compartilhar com vocês uma integração que acabei de disponibilizar para equipamentos Tholz Smart v2 (modelos com comunicação via socket).
Repositório: GitHub - felippepuhle/tholz-hass-integration: Home Assistant + Tholz integration
Aviso importante: esta é uma integração não oficial. Não tenho nenhuma ligação com a Tholz. Já entrei em contato com eles em outros momentos, mas como não demonstraram interesse, decidi resolver por conta própria e liberar para a comunidade. Escrevi a integração no meu tempo livre, da melhor forma possível (Python não é meu forte
), sem qualquer objetivo financeiro.
Atualmente só consigo testar nos modelos Smart Pool v2 e Smart Heat v2, então não posso garantir funcionamento em outros equipamentos.
Ainda não está no HACS (estou trabalhando nisso), mas já pode ser instalado via Custom Repository.
O repositório está aberto para contribuições - se alguém quiser colaborar é só me chamar. Abraços!
parabens man, ja tenho minha integração funcionando aqui mas vou testar a sua depois e passo o feedback
Show! Usou o SmartHeat v2? O meu é o SmartHeat V1 com o SmartConnect v2… vou testar e passo aqui como ficou.
EXCELENTE Felipe, muitooo obrigado por compartilhar.
Coloca la no Github um “Buy me a Coffee” !!
Tenho o Smart Pool v2 e Smart Heat v2, ambos funcionaram aqui no HA.
No Smart Heat, ele deixa eu ligar/desligar e setar a temperatura de acionamento do apoio eletrico. MUITO bom.
Mas ele não está mostrando a temperatura atual do Boiler.
Novamente, parabéns pela integração e muito obrigado.
O intuito nem é ganhar dinheiro, é só compartilhar mesmo para tentar ajudar! Pelo que vi, os seus firmwares parecem ser diferentes dos meus (teoricamente ambos são v2, trabalhei assumindo que o firmware principal seria o mesmo para todos Heats, Pools, etc)… Já entrei em contato com a Tholz algumas vezes pedindo essa integração, mas eles não avançaram nesse ponto. Acho que vai acabar ficando para nós, da comunidade, ir expandindo essa configuração. Você consegue entrar em contato com o suporte deles pedindo mais informações sobre como mapear o seu caso? Vou fazer o mesmo amanhã!
Posso também te passar um script em Python do getDevice pra ver se a gente consegue descobrir essas diferenças. Mas já adianto que pode ser um pouco trabalhoso… no meu caso levei várias horas/dias até conseguir fazer tudo funcionar como esperado.
Já identifiquei a falha no meu código: realmente não adicionei suporte aos sensores do termostato porque não estava documentado e não tinha certeza sobre a função de cada um… Você poderia confirmar para mim quais são os sensores disponíveis (t1, t2, t3, t4, t5) e o que cada um representa? Assim consigo atualizar a configuração e liberar uma nova release para você testar.
Opa Felippe, obrigado pelo apoio. Consegui pegar um getDevice do meu SmartHeat.
{“command”:“getDevice”,“response”:{“id”:31516,“reset”:0,“error”:0,“updating”:false,“firmware”:“P858V4”,“firmwareSec”:“P866V1”,“timezone”:-10800,“heatings”:{“heat0”:{“on”:false,“onAut”:true,“opMode”:0,“type”:10,“step”:10,“sp”:380,“maxSp”:1000,“minSp”:0,“aux”:1,“t1”:600,“t2”:null,“t3”:null,“t4”:null,“t5”:null}}}}
No caso, t1 seria a temperatura da agua no Boiler, que está em 60.0 no aplicativo.
Se tiver mais alguma maneira que eu possa ajudar.
Acabei de liberar a v0.2.1 adicionando o t1 como sensor de temperatura para aquecimentos do tipo termostato. A entidade de water heater também passa a usar o t1 como temperatura atual!
Excelente Felippe. Atualizei e está funcionando perfeitamente.
Sem querer abusar, tenho mais um feedback / Feature request.. ![]()
No Smart Pool, tenho 2 motores alem do aquecimento. Um para filtro e um para cascata.
Pelo o que entendi, é o “outputs”:{“out0”:{“id”:0,“on”:true,“onAut”:false}
FILTRO LIGADO
{“command”:“getDevice”,“response”:{“id”:11462,“reset”:0,“error”:0,“updating”:false,“firmware”:“P858V4”,“firmwareSec”:“P844V1”,“timezone”:-10800,“leds”:{“led0”:{“on”:false,“onAut”:false,“effect”:255,“brightness”:100,“speed”:50,“type”:1,“color”:[255,255,255]}},“heatings”:{“heat0”:{“on”:false,“onAut”:false,“opMode”:0,“type”:0,“step”:10,“sp”:350,“maxSp”:400,“minSp”:150,“aux”:null,“t1”:360,“t2”:250,“t3”:null,“t4”:null,“t5”:null},“heat1”:{“on”:false,“onAut”:false,“opMode”:0,“type”:0,“step”:0,“sp”:0,“maxSp”:0,“minSp”:0,“aux”:0,“t1”:0,“t2”:0,“t3”:0,“t4”:0,“t5”:0}},“outputs”:{“out0”:{“id”:0,“on”:true,“onAut”:false},“out1”:{“id”:10,“on”:false,“onAut”:false},“out2”:{“id”:11,“on”:false,“onAut”:false},“out3”:{“id”:12,“on”:false,“onAut”:false},“out4”:null}}}
FILTRO DESLIGADO
{“command”:“getDevice”,“response”:{“id”:11462,“reset”:0,“error”:0,“updating”:false,“firmware”:“P858V4”,“firmwareSec”:“P844V1”,“timezone”:-10800,“leds”:{“led0”:{“on”:false,“onAut”:false,“effect”:255,“brightness”:100,“speed”:50,“type”:1,“color”:[255,255,255]}},“heatings”:{“heat0”:{“on”:false,“onAut”:false,“opMode”:0,“type”:0,“step”:10,“sp”:350,“maxSp”:400,“minSp”:150,“aux”:null,“t1”:360,“t2”:260,“t3”:null,“t4”:null,“t5”:null},“heat1”:{“on”:false,“onAut”:false,“opMode”:0,“type”:0,“step”:0,“sp”:0,“maxSp”:0,“minSp”:0,“aux”:0,“t1”:0,“t2”:0,“t3”:0,“t4”:0,“t5”:0}},“outputs”:{“out0”:{“id”:0,“on”:false,“onAut”:false},“out1”:{“id”:10,“on”:false,“onAut”:false},“out2”:{“id”:11,“on”:false,“onAut”:false},“out3”:{“id”:12,“on”:false,“onAut”:false},“out4”:null}}}
Da para aciona-los pelo HA?
Dá sim, era pra estar funcionando… boa parte desse código rodou localmente por meses. Tive que reescrever uma parte pra deixá-lo mais genérico antes de compartilhar, então provavelmente quebrei algo nesse processo. Meu QDC está em manutenção e o Smart Pool desligado, mas vou verificar se consigo identificar onde está a falha.
Aproveitando: você consegue tirar uma foto do seu Smart Heat e me enviar? Notei que o seu firmware é diferente do meu, e gostaria de mapear essa versão para que o nome do equipamento também seja exibido corretamente na integração.
Experimenta testar a v0.2.2, deve estar funcionando agora! Assim que meu QDC ficar pronto, consigo testar também. A ideia é terminar a integração da entidade de LED quando eu instalar ele também.
@felippepuhle o meu ficou dessa forma:
@felippepuhle a que eu fiz pelo Node-RED/MQTT cheguei nisso:





