Je weet dat dit een Homey forum is, niet HA…
Geinstalleerd en portscan uitgevoerd, alleen port 80 wordt gebruikt. De webinterface doet nog wat lastig in firefox. In eerste instantie wilde de css niet laden bij mij… Kan te maken hebben met de mime-types die niet kloppen, Chrome vindt dit niet erg.
Het script van ‘http://10.0.4.174/javascript/thermostat.js’ is geladen, hoewel het MIME-type ervan (‘’) geen geldig JavaScript-MIME-type is.
De source van thermostat.js bekijken in ff kan niet, wel in Chrome. Uitlezen waardes in thermostat.xml:
<thermostat>
<setpoint>15.0</setpoint>
<temperature>15.9</temperature>
<outside>---</outside>
<pause>0</pause>
<heating>0</heating>
</thermostat>
Met simpele ajaxcalls kan de temperatuur ingesteld worden en kan het programma gepauzeerd en gestart worden:
const url = "thermostat.xml";
let setpoint = 20;
let timeoutId = null, repeatId;
function updateTemperature(xmlData) {
for (let item of xmlData.children) {
if (item.tagName == "setpoint") {
if (timeoutId !== null) continue;
setpoint = parseFloat(item.textContent);
}
let w = document.getElementById(item.tagName);
if (w) {
w.innerText = item.textContent;
}
}
clearTimeout(repeatId);
repeatId = setTimeout(() => {
newAJAXCommand(url, updateTemperature);
}, 30000);
}
function updateSetpoint(delta) {
clearTimeout(timeoutId);
// Apply lower and upper limits
setpoint = Math.min(30, Math.max(5, setpoint + delta));
document.getElementById('setpoint').innerText = setpoint.toFixed(1);
timeoutId = setTimeout(() => {
newAJAXCommand(url, updateTemperature, false, "svset=" + setpoint);
timeoutId = null;
}, 1000);
}
function updatePause() {
newAJAXCommand(url, updateTemperature, false, "pause=-1");
}
window.addEventListener("load", () => {
newAJAXCommand(url, updateTemperature);
});
En rechtstreeks posten vanuit je browser is ook mogelijk met de javascript-console:
await fetch("http://10.0.4.174/thermostat.xml", {
"credentials": "omit",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0",
"Accept": "*/*",
"Accept-Language": "nl,en-US;q=0.7,en;q=0.3",
"Content-Type": "text/plain;charset=UTF-8",
"Sec-GPC": "1"
},
"referrer": "http://10.0.4.174/",
"body": "svset=14.5",
"method": "POST",
"mode": "cors"
});
Helaas is het maken van een http-post request vanuit een Homey flowcard mij nog niet gelukt…
Pfff … Ik moet ook niet proberen xml te sturen ondanks dat de flowcard dit als voorbeeld geeft… Gewoon “svset=17” en verder geen speciale headers nodig…
Toggle pauze gaat door “pause=-1” te sturen…
Dus met de nieuwe firmware kan je Homey de temperatuur laten regelen…
Temperatuur instellen kan alleen als de TS niet op pauze-stand staat. En we willen dit natuurlijk aan een device gekoppeld hebben. Ik heb wat scripts gemaakt in Homey-script.
Pause aan of uit
Allereerst een device aangemaakt om pause “aan” of “uit” te zetten, gekoppeld aan een knop (bij mij in VD dus):
met de Homey-script-code (het ip-adres in de url moet aangepast worden):
// Define URLs (replace urlSetPause with the actual URL)
const url = 'http://10.0.4.174/thermostat.xml'; // URL for fetching data
console.log("starting");
main();
async function main() {
try {
const pauseValue = await fetchData(url);
console.log(pauseValue);
// Update pause value if needed
if (pauseValue === "1") {
await setPause(url, "-1");
console.log("Pause toggled to disabled (pause=-1 sent).");
} else {
console.log("Pause is not currently active. No action taken.");
}
} catch (error) {
console.error("Error:", error);
}
}
// Function to fetch data (replace logic with actual temperature and setpoint extraction)
async function fetchData(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const xmlString = await response.text();
const pauseValue = xmlString.match(/<pause>(.*)<\/pause>/)[1];
console.log(pauseValue);
return pauseValue; // Return only pauseValue
}
// Function to set pause value (replace with actual logic if needed)
async function setPause(url, pauseValue) {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'text/plain' }, // Set Content-Type to plain text
body: `pause=${pauseValue}` // Send pause directly in the body
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.ok}`);
}
}
en om pauze aan te zetten:
met de Homeyscript-code (het ip-adres in de url moet aangepast worden):
// Define URLs (replace urlSetPause with the actual URL)
const url = 'http://10.0.4.174/thermostat.xml'; // URL for fetching data
console.log("starting");
main();
async function main() {
try {
const pauseValue = await fetchData(url);
console.log(pauseValue);
// Update pause value if needed
if (pauseValue === "0") {
await setPause(url, "-1");
console.log("Pause toggled to enabled (pause=-1 sent).");
} else {
console.log("Pause is currently active. No action taken.");
}
} catch (error) {
console.error("Error:", error);
}
}
// Function to fetch data
async function fetchData(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const xmlString = await response.text();
const pauseValue = xmlString.match(/<pause>(.*)<\/pause>/)[1];
console.log(pauseValue);
return pauseValue; // Return only pauseValue
}
// Function to set pause value (replace with actual logic if needed)
async function setPause(url, pauseValue) {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'text/plain' }, // Set Content-Type to plain text
body: `pause=${pauseValue}` // Send pause directly in the body
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.ok}`);
}
}
temperatuur wijzigen
En een Virtual Device Thermostaat aangemaakt. Bij wijziging van de instel-temperatuur wordt een Homeyscript aangeroepen met de tag setpoint. Dan wordt eerst de pause uitgezet (als deze aan staat) en vervolgens wordt de temperatuur ingesteld:
met de Homeyscript-code (het ip-adres in de url moet aangepast worden):
const newtemp = JSON.parse(args[0]);
const url = 'http://10.0.4.174/thermostat.xml'; // URL for setting setpoint
main();
async function main() {
try {
// Fetch current data (replace logic with actual temperature and setpoint extraction)
const { temperature, setpoint, pauseValue } = await fetchData(url);
// Update pause value if needed
if (pauseValue === "1") { // Send "-1" only if pause is currently active
await setPause(url, "-1");
console.log("Pause value set to 0 (disabled).");
} else {
console.log("Pause is not currently active. Skipping update.");
}
// Set the new setpoint
await setSetpoint(url, newtemp);
console.log(`Setpoint updated to ${newtemp}`);
// Log current values
console.log(`Temperature: ${temperature}`);
console.log(`Setpoint: ${setpoint}`);
console.log(`Pause value: ${pauseValue}`);
} catch (error) {
console.error("Error:", error);
}
}
// Function to fetch data (replace logic with actual temperature and setpoint extraction)
async function fetchData(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const xmlString = await response.text();
//the XML is simple and always the same
const temperature = xmlString.match(/<temperature>(.*)<\/temperature>/)[1];
const setpoint = xmlString.match(/<setpoint>(.*)<\/setpoint>/)[1];
const pauseValue = xmlString.match(/<pause>(.*)<\/pause>/)[1];
return { temperature, setpoint, pauseValue }; // Return all three values
}
// Function to set pause value
async function setPause(url, pauseValue) {
//setting the pause using HTTP POST as plain text
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'text/plain' }, // Set Content-Type to plain text
body: `pause=${pauseValue}` // Send pause directly in the body
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.ok}`);
}
}
// Function to set setpoint (replace logic with actual setpoint update logic)
async function setSetpoint(url, setpointValue) {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'text/plain' }, // Set Content-Type to plain text
body: `svset=${setpointValue}` // Send svset directly in the body
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.ok}`);
}
}
temperatuur weergeven
Tenslotte moet natuurlijk de gemeten temperatuur wel weergegeven worden op de Virtual Thermostaat. Direct instellen vanuit Homeyscript kan niet (temperature is niet settable). In Homeyscript moet de temperatuur naar een variabele geschreven worden (in mijn geval naar “TS-gemeten”, die wordt elke 50 seconde uitgevoerd):
met de Homeyscript-code (het ip-adres in de url en de variabele-naam moet aangepast worden):
const url = 'http://10.0.4.174/thermostat.xml'; // URL for setting setpoint
const VariableName = "TS-gemeten"; //Name of variable which holds temperature
const vars = await Homey.logic.getVariables();
const myVar = _.find(vars, (o) => o.name === VariableName);
main();
async function main() {
try {
// Fetch current data (replace logic with actual temperature and setpoint extraction)
const { temperature} = await fetchData(url);
console.log ("setting temperature to ",temperature," from ", myVar.value);
await Homey.logic.updateVariable({id: myVar.id, variable: {value: Number(temperature)}})
} catch (error) {
console.error("Error:", error);
}
}
// Function to fetch data (replace logic with actual temperature and setpoint extraction)
async function fetchData(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const xmlString = await response.text();
//the XML is simple and always the same
const temperature = xmlString.match(/<temperature>(.*)<\/temperature>/)[1];
console.log ("in functie");
console.log (temperature);
return { temperature}; // Return
}
En vervolgens, als de waarde van de variable “TS-gemeten” is veranderd, dan moet de temperatuur op de thermostaat worden geupdate:
update : Response from Google-Gemini which helped me with the code :
I’m glad to hear that everything works and you’ve successfully posted your findings and code on the Homey community forum! Sharing solutions with others is a great way to contribute to the community.
I hope this project helps you maintain control over your thermostat even after Thermosmart’s closure.
Een tip:
Als je i.p.v. Virtuele Apparaten de zg. Geavanceerde Virtuele Apparaten gebruikt, heb je geen flows nodig om de waardes te ‘spiegelen’.
Voorbeeld voor
TS-gemeten