Anyway to write a plugin variable on a label control?

Hi

I built a plugin to do a reverse geocoding via google maps API when I press a button but since plugin are completely dissociated from other plugin I can’t find a way to send the result to a virtual device so I have no clue how I could display the result of my plugin on THR screen.

Anyone would have a brilliant idea on how I could do this

Thanks

If you are able to write the Value as text in a file it would be easy.
Put your question exact at it is in ChatGPT and you will surprised

Thanks for this but my search does not provide me with any way to write to a text file with THR

Ok then I misunderstood you - sorry. You talk about a THR plugin which you wrote and try to display the result in THR - is this right?
Can you post the plugin code?

Here is the plugin code:

I would like to display the data of “formattedAddress” to a label control in THR

plugin.Name = "HTTPExample";
plugin.OnChangeRequest = onChangeRequest;
plugin.OnConnect = onConnect;
plugin.OnDisconnect = onDisconnect;
plugin.OnPoll = onPoll;
plugin.OnSynchronizeDevices = onSynchronizeDevices;
plugin.PollingInterval = 1000;

var http = new HTTPClient();

function onChangeRequest(device, attribute, value) {
   
    if (device.Id == "1") {
        switch (value) {
            case "On":
             var  response = http.get("https://maps.googleapis.com/maps/api/geocode/json?latlng=46.01744,-74.07707&location_type=ROOFTOP&result_type=street_address&key=********************",{responseType : "text"});             
            geocodingData = JSON.parse(response.data); 
            formattedAddress = geocodingData.results[0].formatted_address;
	                 
                break;
            case "Off":
               break;
            default:
                break;
        }
    }
    else {
        throw "Commands for this device Id have not been implemented";
    }
}

function onConnect() {
}

function onDisconnect() {
}

function onPoll() {
}

function onSynchronizeDevices() {
    var switch1 = new Device();
    switch1.Id = "1";
    switch1.DisplayName = "Switch 1";
    switch1.Capabilities = ["Switch"];
    switch1.Attributes = [];
    plugin.Devices[switch1.Id] = switch1;
}

Okay, I’ve created a new plugin, or rather two new plugins.
The first is based on yours, where fixed coordinates are preset, which you can change in the plugin settings.
The second one is structured completely differently—once the plugin is installed, you have to create two text fields (longitude/latitude) in which you can enter the coordinates dynamically. Then you need a button to send the coordinates and a label to display the result.
Unfortunately, I’m on the road right now, but I’ll post the code and explanation later.
The API-Key is now in the settings and not hardcoded in the plugin :wink:

Kalle

My ! thanks I am eager to look at these. One thing I forgot to mention in the previous exchange is that the coordinates that I hardcoded in the plugin are dynamics, I mean those are values coming from a HomeAssistant plugin device (input_text.latitude & input_text.longitude) so my question now that i have continued my research is there a way in a plugin to get data from another plugin ???

I guess the question, can we, in the same plugin, get data from a specific http client and write data with a different http client ?

short answer - yes - but let us first test the plugins

here ist the dynamic input plugin code - copy the code and go to devices ->add plugin-> from code and past it in. Now you have to setup in the plugin settings your Google API

  1. create two text fields
  2. in the first textfield set in properties “text” the binding for AddressLookup.LatitudeInput in the second textfield binding AddressLookup.LongitudeInput
  3. create a label with binding AddressLookup.LastAddress
  4. create a button (switch) with binding AddressLookup.switch

Enter Coordinates: Type a latitude value into the first TextBox (which binds to LatitudeInput) and a longitude value into the second TextBox (which binds to LongitudeInput). The plugin automatically stores these new values in its internal attributes via onChangeRequest.

Trigger Action: Tap the Button (which is bound to Device.Address Lookup.Switch).

Result: The plugin runs the geocoding API, and the result is displayed in the Label (bound to LastAddress).

I did not tested the plugin if it works, because I have no API key.

plugin.Name = "GeoCodingDynamicInput"; // NEW NAME to bypass caching

// DEFINITION: Configuration settings

plugin.Settings = [
    {
        name: "GoogleMapsApiKey",
        display: "Google Maps API Key",
        type: "text",
        description: "Your API key for the Google Geocoding Service."
    }
];

// DEFAULT SETTINGS

plugin.DefaultSettings = {
    "GoogleMapsApiKey": ""
};


plugin.OnChangeRequest = onChangeRequest;
plugin.OnConnect = onConnect;
plugin.OnDisconnect = onDisconnect;
plugin.OnPoll = onPoll;
plugin.OnSynchronizeDevices = onSynchronizeDevices;
plugin.PollingInterval = 1000;
var http = new HTTPClient();

/**
 * Executes a command when a device attribute changes.
 */
function onChangeRequest(device, attribute, value) {
    
    if (device.Id == "1") {
        
        // Immediately saves the new value if it's an input field
        if (attribute === "LatitudeInput" || attribute === "LongitudeInput") {
             plugin.Devices[device.Id].Attributes[attribute] = value;
             return; // Update attribute and exit
        }
        
        // --- Core Logic executed only when Switch is toggled ---
        if (attribute === "Switch") {
            // Ensures the Switch status is updated in THR
            plugin.Devices[device.Id].Attributes["Switch"] = value;
            
            // Read the required values from the attributes
            var apiKey = plugin.Settings.GoogleMapsApiKey;
            var latitude = plugin.Devices[device.Id].Attributes["LatitudeInput"];
            var longitude = plugin.Devices[device.Id].Attributes["LongitudeInput"];

            switch (value) {
                case "On":
                    // 1. Validation Checks
                    if (!apiKey || apiKey.length < 10) {
                        plugin.Devices[device.Id].Attributes["LastAddress"] = "Error: API Key missing in settings.";
                        console.error("API Key is missing or invalid in plugin settings.");
                        break;
                    }
                    if (!latitude || !longitude) {
                        plugin.Devices[device.Id].Attributes["LastAddress"] = "Error: Please enter Latitude and Longitude.";
                        console.error("Latitude or Longitude input is missing.");
                        break;
                    }
                
                    try {
                        var url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=" + latitude + "," + longitude + "&location_type=ROOFTOP&result_type=street_address&key=" + apiKey;
                        var response = http.get(url, {responseType : "text"});         
                        
                        var geocodingData = JSON.parse(response.data); 
                        
                        if (geocodingData.results && geocodingData.results.length > 0) {
                            var formattedAddress = geocodingData.results[0].formatted_address;
                            
                            plugin.Devices[device.Id].Attributes["LastAddress"] = "Address: " + formattedAddress;
                            console.log("Address successfully retrieved and stored:", formattedAddress);
                        } else {
                            plugin.Devices[device.Id].Attributes["LastAddress"] = "API Error: No results found.";
                            console.error("API call returned no results.");
                        }
                    } catch(e) {
                        console.error("Error retrieving address:", e);
                        plugin.Devices[device.Id].Attributes["LastAddress"] = "Error retrieving data.";
                    }
                    break;
                    
                case "Off":
                    // Resets the address status when the switch is turned off
                    plugin.Devices[device.Id].Attributes["LastAddress"] = "Switch is Off. Input new coordinates.";
                    break;
                    
                default:
                    break;
            }
        }
    }
    else {
        throw "Commands for this device Id have not been implemented";
    }
}

/**
 * Called when the connection starts.
 */
function onConnect() {
    // Initialization logic goes here
}

/**
 * Called when the connection disconnects.
 */
function onDisconnect() {
    // Cleanup logic goes here
}

/**
 * Called every PollingInterval (1000ms).
 */
function onPoll() {
    // Polling/status update logic goes here
}

/**
 * NEW CORRECT STRUCTURE: Defines attributes as an array of strings (like in the Domoticz plugin)
 * and sets initial values via the Devices Collection, not the Attributes object itself.
 */
function onSynchronizeDevices() {
    var switch1 = new Device();
    switch1.Id = "1";
    switch1.DisplayName = "Address Lookup";
    switch1.Capabilities = ["Switch"];
    
    // 1. DECLARATION: This MUST be an array of attribute names (Strings)
    switch1.Attributes = [
        "LatitudeInput", 
        "LongitudeInput", 
        "LastAddress"
    ]; 
    
    plugin.Devices[switch1.Id] = switch1;
    
    // 2. INITIALIZATION (Optional, but recommended): Sets initial values AFTER assignment
    // to the Devices Collection so THR correctly recognizes the metadata.
    plugin.Devices["1"].Attributes["LatitudeInput"] = "46.01744"; // Example coordinate 1
    plugin.Devices["1"].Attributes["LongitudeInput"] = "-74.07707"; // Example coordinate 2
    plugin.Devices["1"].Attributes["LastAddress"] = "Ready to retrieve";
}

and here is your not really “hardcoded coordinates” plugin code. You can change the coordinates in the seetings of the plugin.

plugin.Name = "GeoCoding";
// DEFINITION: Configuration settings for the API Key and Coordinates

plugin.Settings = [
    {
        name: "GoogleMapsApiKey",
        display: "Google Maps API Key",
        type: "text",
        description: "Your API key for the Google Geocoding Service."
    },
    {
        name: "Latitude",
        display: "Latitude",
        type: "text",
        description: "The latitude (e.g., 46.01744) to query."
    },
    {
        name: "Longitude",
        display: "Longitude",
        type: "text",
        description: "The longitude (e.g., -74.07707) to query."
    }
];

// DEFAULT SETTINGS: Ensures the variables are initialized in the THR Designer

plugin.DefaultSettings = {
    "GoogleMapsApiKey": "",
    "Latitude": "46.01744", // Default coordinate
    "Longitude": "-74.07707" // Default coordinate
};


plugin.OnChangeRequest = onChangeRequest;
plugin.OnConnect = onConnect;
plugin.OnDisconnect = onDisconnect;
plugin.OnPoll = onPoll;
plugin.OnSynchronizeDevices = onSynchronizeDevices;
plugin.PollingInterval = 1000;
var http = new HTTPClient();

/**
 * Executes a command when a device attribute changes (i.e., when the switch is toggled).
 */
function onChangeRequest(device, attribute, value) {
    
    if (device.Id == "1") {
        
        // Ensures the Switch status is updated in THR
        plugin.Devices[device.Id].Attributes["Switch"] = value;
        
        // Retrieve API Key and Coordinates from Plugin Settings
        var apiKey = plugin.Settings.GoogleMapsApiKey;
        var latitude = plugin.Settings.Latitude;
        var longitude = plugin.Settings.Longitude;


        switch (value) {
            case "On":
                if (!apiKey || apiKey.length < 10) {
                    // *** FEHLER-ERGEBNIS HIER GESPEICHERT ***
                    plugin.Devices[device.Id].Attributes["LastAddress"] = "Error: API Key missing in settings.";
                    console.error("API Key is missing or invalid in plugin settings.");
                    break;
                }
            
                try {
                    // API call now uses configurable settings
                    var url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=" + latitude + "," + longitude + "&location_type=ROOFTOP&result_type=street_address&key=" + apiKey;
                    var response = http.get(url, {responseType : "text"});         
                    
                    var geocodingData = JSON.parse(response.data); 
                    
                    if (geocodingData.results && geocodingData.results.length > 0) {
                        var formattedAddress = geocodingData.results[0].formatted_address;
                        
                        // *** ERFOLGREICHES ERGEBNIS HIER GESPEICHERT ***
                        plugin.Devices[device.Id].Attributes["LastAddress"] = "Address: " + formattedAddress;
                        
                        console.log("Address successfully retrieved and stored:", formattedAddress);
                    } else {
                        // *** API-FEHLER HIER GESPEICHERT ***
                        plugin.Devices[device.Id].Attributes["LastAddress"] = "API Error: No results found.";
                        console.error("API call returned no results.");
                    }
                } catch(e) {
                    console.error("Error retrieving address:", e);
                    // *** ALLGEMEINER FEHLER HIER GESPEICHERT ***
                    plugin.Devices[device.Id].Attributes["LastAddress"] = "Error retrieving data.";
                }
                break;
                
            case "Off":
                // Resets the address status when the switch is turned off
                plugin.Devices[device.Id].Attributes["LastAddress"] = "Switch is Off.";
                break;
                
            default:
                break;
        }
    }
    else {
        throw "Commands for this device Id have not been implemented";
    }

}

/**
 * Called when the connection starts.
 */
function onConnect() {
    // Initialization logic goes here
}

/**
 * Called when the connection disconnects.
 */
function onDisconnect() {
    // Cleanup logic goes here
}

/**
 * Called every PollingInterval (1000ms).
 */
function onPoll() {
    // Polling/status update logic goes here
}

/**
 * Creates the virtual device "Geocoding Lookup" and declares the LastAddress attribute.
 */
function onSynchronizeDevices() {
    var device = new Device();
    device.Id = "1";
    device.DisplayName = "Geocoding Lookup";
    device.Capabilities = ["Switch"];
    
    // 1. Deklariert Attribute als Array von Strings
    device.Attributes = [
        "LastAddress" // Das Attribut, das das Ergebnis speichert
    ]; 
    
    plugin.Devices[device.Id] = device;
    
    // 2. Setzt den Initialwert
    plugin.Devices["1"].Attributes["LastAddress"] = "Ready to retrieve";

}

Maybe you could explain what you mean by “HomeAssistant”? I assume you mean the open source software for home automation, right? Perhaps you could explain how the coordinates are provided there, or maybe there is a way to host them and automatically retrieve them from that location via THR.

Ok i’ll explain the overall desired process (Maybe I should have done so at the begining)

I am trying to build an application that will give me the exact address of the position of one of our company truck.

Each truck have devices that return their long/lat on every significant move via an installed app (owntrack) through MQTT to our HomeAssistant automation software.

I want to be able to tap a button on my HomeRemote app that would get the long/lat from device entities on my HomeAssistant server (this is very easy as THR have an integration with HomeAssistant), do the reverse geocoding call to get the adress from google map and then show this address in a label control on my HomeRemote application.

Sure I could have My HomeAssistant to do the API call to google everytime it receives a change from the owntrack app and save that in a HomeAssistant entity and get it when desired in THR but thoses changes happens every 5-10 seconds so having many truck, the cost to do the call every time to the API is something I want to avoid and only do the call when I need it.

I could also have a button on my THR app that would trigger an HTTPrequest to my HomeAssistant server that would turn on an entity and have a listener to that entity that would start the reverse geocoding on HomeAssistant that would save the results in an entity in Home assistant that could be shown on my THR app but that a lot of come and go for nothing.

My idea was
1- In my THR app, I already have access to the long/lat as I use them to physically show a map of the vehicule position at very high level
2- Since I already have those data via HomeAssistant entities tied to THR via the HomeAssistant plugin, I would like to get those data in an HTTPClient Plugin, send the reverse geocoding call directly via the plugin then assign the results to a virtual Device in THR that I could display in a label control.

the problem I see is that there is NO way to get values from another plugin or send something to virtual devices from an HTTPClient plugin

Ok here we go - I hope ChatGPT has done the job as you expected:

here the plugin code and the documentation:

plugin.Name = "ReverseGeocode";
plugin.OnPoll = onPoll;
plugin.OnSynchronizeDevices = onSynchronizeDevices;

plugin.DefaultSettings = { 
    "GoogleApiKey": "YOUR_API_KEY_HERE",
    "PollingInterval": "20000",
    "DebugMode": "off"
};

var http = new HTTPClient();

function log(msg) {
    if (plugin.Settings["DebugMode"] === "on") {
        console.log("[ReverseGeocode] " + msg);
    }
}

function onPoll() {
    plugin.PollingInterval = parseInt(plugin.Settings["PollingInterval"], 10);

    var apiKey = plugin.Settings["GoogleApiKey"];
    if (!apiKey || apiKey === "YOUR_API_KEY_HERE") {
        log("Google API Key missing.");
        return;
    }

    // -------------------------
    // 1. READ HOME ASSISTANT GPS DEVICE
    // -------------------------
    var gpsDevice = plugin.Devices["HA_GPS_DEVICE"];
    if (!gpsDevice) {
        log("Home Assistant GPS device 'HA_GPS_DEVICE' not found.");
        return;
    }

    var lat = gpsDevice.Attributes["HA_LATITUDE_ATTRIBUTE"];
    var lon = gpsDevice.Attributes["HA_LONGITUDE_ATTRIBUTE"];

    if (!lat || !lon) {
        log("Latitude or longitude missing from Home Assistant.");
        return;
    }

    log("Coordinates from HA: " + lat + ", " + lon);

    // -------------------------
    // 2. GOOGLE GEOCODING REQUEST
    // -------------------------
    var url =
        "https://maps.googleapis.com/maps/api/geocode/json?latlng=" +
        encodeURIComponent(lat + "," + lon) +
        "&key=" + encodeURIComponent(apiKey);

    log("Request: " + url);

    var response = http.get(url);

    if (!response || !response.data) {
        log("Google API response empty.");
        return;
    }

    var data = (typeof response.data === "object") ? response.data : JSON.parse(response.data);
    var address = "Address not found";

    if (data.results && data.results.length > 0) {
        address = data.results[0].formatted_address;
    }

    log("Resolved address: " + address);

    // -------------------------
    // 3. UPDATE THR OUTPUT DEVICE
    // -------------------------
    var outDevice = plugin.Devices["THR_OUTPUT_DEVICE"];
    if (!outDevice) {
        log("Output device 'THR_OUTPUT_DEVICE' not found.");
        return;
    }

    outDevice.Attributes["THR_OUTPUT_ATTRIBUTE"] = address;

    log("Updated output device attribute: " + address);
}

function onSynchronizeDevices() {

    // REGISTER HOME ASSISTANT DEVICE PLACEHOLDER
    var gps = new Device();
    gps.Id = "HA_GPS_DEVICE";
    gps.DisplayName = "Home Assistant GPS Device (Placeholder)";
    gps.Attributes = ["HA_LATITUDE_ATTRIBUTE", "HA_LONGITUDE_ATTRIBUTE"];
    plugin.Devices[gps.Id] = gps;

    // REGISTER THR OUTPUT DEVICE PLACEHOLDER
    var geo = new Device();
    geo.Id = "THR_OUTPUT_DEVICE";
    geo.DisplayName = "THR Reverse Geocode Output";
    geo.Attributes = ["THR_OUTPUT_ATTRIBUTE"];
    plugin.Devices[geo.Id] = geo;

    log("Devices synchronized: HA_GPS_DEVICE, THR_OUTPUT_DEVICE");
}

ReverseGeocode_Documentation.txt (2.8 KB)
THR_UI_Elements_Documentation.txt (2.9 KB)

If the code doesn’t work for you, copy and paste it in Gemini or ChatGPT or CoPilot and tell the problem or wishes - this will work most of the time. In this way I have created the most of my plugins for THR.
Let me know how it work or better if it work.

Kalle

Unfortunatly, this is the problem part… Plugin CAN’T read data from other plugin or write data to other plugin

Which error do you get?

This is the workflow how it will work:

1. Home Assistant (HA)

What does Home Assistant do?

Home Assistant provides the raw GPS data, specifically:

  • Latitude
  • Longitude

These values typically come from:

  • a device_tracker entity (car, phone, person, boat, etc.)
  • a GPS sensor
  • any device reporting a location

How does THR receive these values?

Through The Home Remote’s Home Assistant Plugin, which synchronizes HA entities and exposes them inside THR as devices with attributes.

2. The Home Remote – the HA Device

What does the HA device in THR do?

It is the representation of your HA entity inside THR.
It exposes attributes such as:

  • Latitude
  • Longitude

These attributes are read by your reverse geocoding plugin.

3. The Reverse Geocoding Plugin (your custom plugin)

What does the plugin do?

This is the core logic of the entire system.

The plugin is responsible for:

1. Reading the coordinates

From the THR Home Assistant device using the placeholders:

  • HA_GPS_DEVICE
  • HA_LATITUDE_ATTRIBUTE
  • HA_LONGITUDE_ATTRIBUTE

2. Sending a Google Maps Reverse Geocoding Request

Example:
https://maps.googleapis.com/maps/api/geocode/json?latlng=50.11,8.68&key=XXXX

3. Processing the Response

The plugin extracts the formatted human-readable address from the JSON.

4. Writing the Address into a THR Virtual Device

Using the placeholders:

  • THR_OUTPUT_DEVICE
  • THR_OUTPUT_ATTRIBUTE

In short:
The plugin performs all calculations and the API work.

4. The Virtual Device in THR (Output Device)

What does the virtual device do?

It stores the final address string returned by the plugin, for example:

“Friedrich-Ebert-Anlage 26, 60325 Frankfurt am Main, Germany”

The plugin writes this value into the output attribute.

Why is this needed?

Because THR UI controls (Labels, TextBlocks, etc.) can only bind to device attributes, not to plugin variables.

5. The UI in The Home Remote (Labels, Buttons)

What does the UI do?

  • A Label displays the resolved address by binding directly to:
    @Device.THR_OUTPUT_DEVICE.THR_OUTPUT_ATTRIBUTE
  • A Refresh Button (optional) can manually trigger a plugin refresh.

The UI has no logic — it only shows what the plugin provides or invokes a refresh.

6. Google Maps Geocoding API

What does Google do?

Google performs:

  • the reverse geocoding
  • the conversion from raw GPS coordinates into a readable address

Your plugin sends the coordinates to Google and receives a structured JSON response.

:heavy_check_mark: Complete Summary (One Sentence)

Home Assistant provides the coordinates → THR exposes them as a device → the plugin reads the coordinates, sends them to Google, receives the address, writes it to a THR output device → the UI displays the address.

A second option would be to store the data on a simple Python web server and have the plugin read it from there.

step 3, HA devices ARE NOT available to plugin see the author response specifically to an OP quesion

Ok, sorry I give up - maybe you will find a other solution.

Thanks a lot for your help anyway, it forced me to start looking at ChatGpt.
I ended up using a mqttplugin to lift a flag on Home assistant when a button is pressed, wich activate an event to do the reverse geocoding in Home assistant, then the adress is saved in a home assistant entity wich is then grab by the HomeAssistant plugin in homeremote to display the result… LONG way to do a simple task but hey… no choice