Smarthome/J Tuya Binding support for WIFI TUYA Energy Meter 63A with voltage current and leakage protection (Need HELP)

Hi. I am trying to setup this kind of power meter from aliexpress:
3

I have instaled:

  • Openhab 4.1.0
  • Smarthome/J Tuya Binding (v4.0.0) from marketplace
  • SingUp to iot.tuya.com and connect openhab to tuya cloud
  • This power meter was auto discovered by binding and added as thing (online)
  • And this channels was also auto created:

and from this step i have some problems.

Not all channels are working:
Working: V
Not working: X


and some other functions like leakege alarm, over-curent, over/under-voltage alarm not even created in channels tab.

In Tuya Cloud this device configured with DP instructions:

And maybe will help Tuya Cloud device log:


I think the problem is with this dp id “Phase A” and it wierd values.
it is too bad not to have voltage, current, power fron this device(((

It would be great if developer of this cool binding add full support for this device.
if needed i can provide remote access to this device.

May be is there a way to have raw data from tuya device and then use openhab transformation services??

@moderators Can someone move this out of the marketplace? It’s not an add-on.

Done

1 Like

I’m no expert in Tuya, but I use it for a EV Wallbox connection, and binding works great, but the following may help your questions:

Firstly, IMHO, I’m not sure that the OpenHAB binding would ever support ‘devices’ as such. The Tuya EcoSystem is a framework, in which many different Device Manufacturers leverage in different ways. The phaseA example you have is one of those, and its underlying values seems to vary between manufacturers - Supporting all of those would be a massive endeavour, and the generic approach seems to work well.

For the channels which are not working, it appears that Tuya developers can use a generic device type, and but they may not necessarily support (update) all the channels in there. Start your debugging in the Tuya cloud. If you see the values coming in/updating to there, then move your focus to the openhab binding/channels/items etc. If you aren’t seeing the values updating in Tuya Cloud (even though they are there), your problem isn’t in openHAB…

For your ‘PhaseA’ issue, I may be able to help. My EV Wallbox had a field called ‘PhaseA’ (those with 3 phase units will have equivalent PhaseB & PhaseC fields). They have gone a bit economic, and packed in Voltage, Current & Power into one field, I assume to save on Tuya service costs?

  • This field is Hex data encoded into Base64

  • I use a Javascript rule to decode this field, on every update, and write the value to 3 separate openHAB items

  • Once you decode the Base64, you can pull the values from the resulting Hex string (‘payload’) as follows (ECMA2021 JS):

    • var voltage = (parseInt(payload.slice(1,4),16)/10); //Volts

    • var current = (parseInt(payload.slice(6,10),16)/1000); //Amps

    • var power = (parseInt(payload.slice(12,16),16)); //Watts

But noting that you will need to play with the ‘Slices’ and ‘Multipliers’ for your device - These do seem to vary between manufacturers. Uncomment the logging in the below JS script, and you should be able to manipulate the script until you start seeing values in your openHAB items, which match those in your Tuya App.

if(event.itemName != undefined) 
{
 //Stops script from running if triggered via OpenHAB console - Event would be undefined, and fail

  
/* Convert base64 data to hex string. Taken from https://stackoverflow.com/a/57909068/893578
 *   txt : Base64 string.
 *   sep : Hex separator, e.g. '-' for '1a-2b-3c'.  Default empty.
 */  
const base64ToHex = ( () => {
   // Lookup tables
   const values = [], output = [];

   // Main converter
   return function base64ToHex ( txt, sep = '' ) {
      if ( output.length <= 0 ) populateLookups();
      const result = [];
      let v1, v2, v3, v4;
      for ( let i = 0, len = txt.length ; i < len ; i += 4 ) {
         // Map four chars to values.
         v1 = values[ txt.charCodeAt( i   ) ];
         v2 = values[ txt.charCodeAt( i+1 ) ];
         v3 = values[ txt.charCodeAt( i+2 ) ];
         v4 = values[ txt.charCodeAt( i+3 ) ];
         // Split and merge bits, then map and push to output.
         result.push(
            output[ ( v1 << 2) | (v2 >> 4) ],
            output[ ((v2 & 15) << 4) | (v3 >> 2) ],
            output[ ((v3 &  3) << 6) |  v4 ]
         );
      }
      // Trim result if the last values are '='.
      if ( v4 === 64 ) result.splice( v3 === 64 ? -2 : -1 );
      return result.join( sep );
   };

   function populateLookups () {
      const keys = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
      for ( let i = 0 ; i < 256 ; i++ ) {
         output.push( ( '0' + i.toString( 16 ) ).slice( -2 ) );
         values.push( 0 );
      }
      for ( let i = 0 ; i <  65 ; i++ )
         values[ keys.charCodeAt( i ) ] = i;
   }
} )();

  
var dataStr = items['EV_Charger_Phase_A'].rawState.toFullString();
var Base64 = Java.type('java.util.Base64');
var dec64 = Base64.getDecoder();
var hex64 = Base64.getDecoder
var payload = base64ToHex(dataStr);

var voltage = (parseInt(payload.slice(1,4),16)/10);
var current = (parseInt(payload.slice(6,10),16)/1000);
var power = (parseInt(payload.slice(12,16),16));
 
items['EV_Charger_Voltage'].sendCommandIfDifferent(voltage);  
items['EV_Charger_Current'].sendCommandIfDifferent(current);  
items['EV_Charger_Power'].sendCommandIfDifferent(power);  
  
 //console.log("Phase_A - datastr: "+dataStr); 
 //console.log("Phase_A - hex: "+payload);
 //console.log("Voltage: "+payload.slice(1,4));
 //console.log("Current: "+payload.slice(6,10));
 //console.log("Power: "+payload.slice(12,16));
 
 //console.log("Voltage: "+voltage+" V");
 //console.log("Current: "+current+" A");
 //console.log("Power: "+power+" W");
 
   
}

Good luck

I don’t understand to which item I should use this script? There is no “PhaseA” channel in my openhab((, but in tuya cloud it is.

Do you have channels “PhaseA”, “PhaseB” and “PhaseC” in your setup with EV Wallbox device?

Hi Alexei,
Yes - There is a ‘Phase A’ Channel for my EV Wallbox Device, which I have linked to an item ‘EV_Charger_Phase_A’

Any time that item is updated from Tuya, the script runs and then updates the Power/Current/Voltage items I also created. (Whilst Tuya also has a Phase B & C for 3 phase versions of my charger, I don’t use them - Its a single phase Wallbox, but the principal would be identical).

So starting with Tuya, you can see the Items in the Standard Status Set:

And then my Phase A OpenHAB Channel on the EV Wallbox ‘Thing’

I had to add/configure it manually (Add Channel → String)
image

I had to play around with the DP number when setting up the channel. I used the Standard Status Set Debug Page on Tuya IoT page for a clue of which DP number it was, but to be honest it was not logical… it almost feels like the DP numbers start at 2 for my device (It was the 5th device down on the Debug page, but its ‘DP6’ for my ‘Phase A’ channel)

And just for completeness, you can see the Tuya Logs (probably similar to yours) with the values coming in there.

Hope that helps.

1 Like

Hi Alexei - I have tidied up the Script I provided earlier, and made it a bit more Generic, and hopefully this is usable for your device without modification.

Instructions for use are within the comments of the below script, however in summary, all you need to do is:

  • Create the Source item (‘Raw’ Tuya device) and link the associated Phase_A Tuya channel to it
  • Create the 3 target items (required naming convention listed in instructions below)
  • Setup Custom MetaData for the ‘slices’ within which the Data is held. This may vary from Tuya device to device. The Metadata is set in the ‘Raw’ source data item, for each item you want to include in this rule
  • Create the Rule, and paste in the below Javascript
  • Probably enable debug mode for this script initially, so you can get more insight into the values being pulled through, and once they start matching your Tuya App, probably ok to turn it off

I would note that I run OpenHAB on a reasonably powerful system, so not sure what the overheads are like for grabbing the Metadata on each run (no impact on mine anyway), or if it’s even an OK practice, but if its a problem, you can look to caching those values on the first run, and using those instead.

Cheers - Glen

/*
This ECMA2021 script will convert Raw Data from Tuya 'Phase_A' (Or Phase B/C) data, which is Base64 encoded Hex data, and update Voltage, Current & Power items 
Some Tuya devices 'encode' Voltage, Current & Phase into a single 'Raw' Tuya field. This will look something like CP8AdqwAGvQ= (or AAAAAAAAAAA= if all the values are 0)

It should work with multiple device types (including Power Meters), but it has been tested with a EV Wallbox. You may need to adjust the 'slice' start and end positions for Voltage, Power & Current to suit your device. 
Enable debugging first for this script (Via Karaf Console), to give you some helpful clues, (log:set debug org.openhab.automation.script.ui.yourscriptname) then start having a play with the values until it matches what you see in the Tuya App.

Credit to those in the following forum chat who published the general protocol details: https://github.com/tuya/tuya-home-assistant/issues/495
And credit to the to provider of the base64ToHex function, which was taken from taken from https://stackoverflow.com/a/57909068/893578 

To get this working:
- Tuya Channel/Raw item Setup
  - Within your Tuya device 'Thing', Setup an Channel for the 'Raw' Phase Data, which will be updated by Tuya. You will probably need to set this up manually (Add Channel -> Type: String)
  - Have a play around with the 'DP' number of the above channel, using the Tuya Cloud State definitions for some clues
  - Link this channel to a String Item, for the following example we will call this Device_Phase_A
- Target Item Setup 
  - This rule assumes you have created the 3 following (number) items setup for EACH triggering item (same name as the Raw Item, with the following suffixes):
    - _voltage
    - _current
    - _power
  - So for this example, you will have setup the following 3 number items
    - Device_Phase_A_voltage
    - Device_Phase_A_current
    - Device_Phase_A_power
- Rule Setup
  - Create a rule with 'When an item value changes' using Device_Phase_A fot this example, and the Action being 'Run a ECMA2021 script'
  - Paste this script into the ECMA2021 sript section
  - Note, if you have multiple devices, or a device with 3 phases, you can add then to the triggering section of the same rule. I only have one single phase device, so unable to test this theory
- Parameter Setup:
  - Add the following Custom Metadata to the Raw Data Item (for this example Device_Phase_A). I have included the values I use for my EV Wallbox Device in Brackets. These values may need to be adjusted for your specific devicee:
    - voltage_slice_start_position (1)
    - voltage_slice_end_position (4)
    - voltage_multiplier (10)
    - current_slice_start_position (6)
    - current_slice_end_position (10)
    - current_multiplier (1000)
    - power_slice_start_position (12)
    - power_slice_end_position (16)
    - power_multiplier (1) // In this instance, there is no mutiplier, so just use the value '1'
*/

if(event.itemName != undefined) 
{
 //Stops script from running if triggered via OpenHAB console - Event would be undefined, and fail

  //Display Parameters from Item Metadata for Debug
  console.debug(items[event.itemName].name+" : voltage_slice_start_position = "+items[event.itemName].getMetadata("voltage_slice_start_position").value);
  console.debug(items[event.itemName].name+" : voltage_slice_end_position = "+items[event.itemName].getMetadata("voltage_slice_end_position").value);
  console.debug(items[event.itemName].name+" : voltage_multiplier = "+items[event.itemName].getMetadata("voltage_multiplier").value);
  console.debug(items[event.itemName].name+" : current_slice_start_position = "+items[event.itemName].getMetadata("current_slice_start_position").value);
  console.debug(items[event.itemName].name+" : current_slice_end_position = "+items[event.itemName].getMetadata("current_slice_end_position").value);
  console.debug(items[event.itemName].name+" : current_multiplier = "+items[event.itemName].getMetadata("current_multiplier").value);
  console.debug(items[event.itemName].name+" : power_slice_start_position = "+items[event.itemName].getMetadata("power_slice_start_position").value);
  console.debug(items[event.itemName].name+" : power_slice_end_position = "+items[event.itemName].getMetadata("power_slice_end_position").value);
  console.debug(items[event.itemName].name+" : power_multiplier = "+items[event.itemName].getMetadata("power_multiplier").value);
 
  
/* Convert base64 data to hex string. Taken from https://stackoverflow.com/a/57909068/893578
 *   txt : Base64 string.
 *   sep : Hex separator, e.g. '-' for '1a-2b-3c'.  Default empty.
 */  
const base64ToHex = ( () => {
   // Lookup tables
   const values = [], output = [];

   // Main converter
   return function base64ToHex ( txt, sep = '' ) {
      if ( output.length <= 0 ) populateLookups();
      const result = [];
      let v1, v2, v3, v4;
      for ( let i = 0, len = txt.length ; i < len ; i += 4 ) {
         // Map four chars to values.
         v1 = values[ txt.charCodeAt( i   ) ];
         v2 = values[ txt.charCodeAt( i+1 ) ];
         v3 = values[ txt.charCodeAt( i+2 ) ];
         v4 = values[ txt.charCodeAt( i+3 ) ];
         // Split and merge bits, then map and push to output.
         result.push(
            output[ ( v1 << 2) | (v2 >> 4) ],
            output[ ((v2 & 15) << 4) | (v3 >> 2) ],
            output[ ((v3 &  3) << 6) |  v4 ]
         );
      }
      // Trim result if the last values are '='.
      if ( v4 === 64 ) result.splice( v3 === 64 ? -2 : -1 );
      return result.join( sep );
   };

   function populateLookups () {
      const keys = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
      for ( let i = 0 ; i < 256 ; i++ ) {
         output.push( ( '0' + i.toString( 16 ) ).slice( -2 ) );
         values.push( 0 );
      }
      for ( let i = 0 ; i <  65 ; i++ )
         values[ keys.charCodeAt( i ) ] = i;
   }
} )();

  
var dataStr = items[event.itemName].rawState.toFullString();
var Base64 = Java.type('java.util.Base64');
var dec64 = Base64.getDecoder();
var payload = base64ToHex(dataStr);

var voltage = (parseInt(payload.slice(items[event.itemName].getMetadata("voltage_slice_start_position").value,items[event.itemName].getMetadata("voltage_slice_end_position").value),16)/items[event.itemName].getMetadata("voltage_multiplier").value);
var current = (parseInt(payload.slice(items[event.itemName].getMetadata("current_slice_start_position").value,items[event.itemName].getMetadata("current_slice_end_position").value),16)/items[event.itemName].getMetadata("current_multiplier").value);
var power = (parseInt(payload.slice(items[event.itemName].getMetadata("power_slice_start_position").value,items[event.itemName].getMetadata("power_slice_end_position").value),16)/items[event.itemName].getMetadata("power_multiplier").value);
 
items[event.itemName+'_voltage'].sendCommandIfDifferent(voltage);  
items[event.itemName+'_current'].sendCommandIfDifferent(current);  
items[event.itemName+'_power'].sendCommandIfDifferent(power);  
  
 console.debug(event.itemName+'Phase_A - datastr: '+dataStr); 
 console.debug(event.itemName+'Phase_A - hex: '+payload);
 console.debug(event.itemName+'Voltage Slice: '+payload.slice(items[event.itemName].getMetadata("voltage_slice_start_position").value,items[event.itemName].getMetadata("voltage_slice_end_position").value));
 console.debug(event.itemName+'Current Slice: '+payload.slice(items[event.itemName].getMetadata("current_slice_start_position").value,items[event.itemName].getMetadata("current_slice_end_position").value));
 console.debug(event.itemName+'Power Slice: '+payload.slice(items[event.itemName].getMetadata("power_slice_start_position").value,items[event.itemName].getMetadata("power_slice_end_position").value));  
 
 console.debug(event.itemName+'Voltage: '+voltage+' V');
 console.debug(event.itemName+'Current: '+current+' A');
 console.debug(event.itemName+'Power: '+power+' W');
  
2 Likes

Thanks a lot)) Your script provided earlier works without any change in it)) Even ‘Phase A’ channel works with DP6 also))
Now I can use voltage, current and power in my openhab)

I’m also trying to configure “Alarm settings” of my device in this way with this Base64>HEX>“normal” transformation script))

1 Like

Hi Alexei -
With regard to:

Whilst I may not be able to help with specifics for your device, I’m just wondering what your use-case is for this field. The way I understand it this is a field used to set alarm thresholds and conditions (or reading the currently configured thresholds), but does not present the alarm flags themselves.

If this (threshold conditions) is something you want to set & forget, probably using the Tuya app is (hopefully) the easiest way to set them. And if not available in the App, you may be able to set via the Tuya IoT debug page?

But if you do have a use case where you want to update them from OpenHAB, then you virtually have to turn my script completely around and would (as a guess) be “Items>Hex>Base64”. And that also comes with another warning - Does your use case require frequent updates? I have had a device previously which through a firmware fault, was continually updating values in Non-Volatile Storage, which the vendor needed to replace the whole device due to wearing this out pretty quickly (Like think somewhere between 5000 and 10000 rated write cycles). No idea what is inside your device, but I also have other devices which warn against frequent updates.

If however you are happy to set alarm thresholds from the Tuya App, and you are just trying to get the alarm status, that would be in something like the Tuya ‘fault’ bitmap field. If that is the case, then the following script I wrote for another device type, to update OpenHAB items based on bitmap values may be of help to you?:

zflags = items.getItem("ZelioFlags_Output").state;
setState("ZelioHWCElement",0,zflags);
setState("ZelioUnderfloorCirc",1,zflags);
setState("ZelioRingMainCirc",2,zflags);
setState("ZelioSolarCirc",3,zflags);
setState("ZelioWBRumpusCirc",4,zflags);
setState("ZelioWBLivingCirc",5,zflags);
setState("ZelioAlarmArmed",6,zflags);
setState("ZelioGarageNorthDoorClosed",8,zflags);
setState("ZelioGarageSouthDoorClosed",9,zflags);
setState("ZelioWoodshedDoorClosed",10,zflags);

function setState(item,bitpos,zflags) {
/* Function to update status of items, based on a bit status
Parameters as follows:
 - item - The name of the OpenHAB item to update with ON or OFF (Expects Item to be a Switch type)
 - bitpos - The bit position in the source data to read
 - zflags - The source data from which to read the Status
*/ 
  switch(getBit(zflags,bitpos)){
    case "ON" :
        items[item].sendCommandIfDifferent("ON");
        console.debug("Changing state to ON for:",items.getItem(item).name);
      break;
    case "OFF" :
        items[item].sendCommandIfDifferent("OFF");
        console.debug("Changing state to OFF for:",items.getItem(item).name);
      break;    
    default:
      console.log("Unknown state for:",items[item].name);
  } 
}

function getBit(number, bitPosition) {
// Function to read individual bits and return status
  return (number & (1 << bitPosition)) === 0 ? "OFF" : "ON";
}

Noting the following:

  • It is a quick and dirty script I wrote when I was starting out with JS scripting in OpenHAB, so everything is hardcoded in there (Would do it differently now)
  • You set this rule up to run every time the Tuya ‘fault’ item changes, and also on start-level 100 (I needed that for my use case anyway)
  • Just update the ‘setState’ lines with the bit position, and the associated OpenHAB switch item you want to update (In your case it might be a overcurrent alarm switch, under voltage switch, over voltage switch), and add more lines as needed (Are there 16 flags for your device?)
  • And a final disclaimer - I have NOT used the Tuya fault bitmap field for my EV wallbox. All the bits represent fairly serious issues (not threshold limits like your device does), so I really have not bothered setting this up, as there is no way of testing this… safely :slight_smile:

Good luck, and hope this helps.

Just did it))) Now i can set All Alarms like in tuya app)





Now Openhab and this device have two-way connection:
when configure device by bottons or by tuya app → OpenHab receive updates to all this statest.
when configure from OpenHab → tuya app receive updates to all this statest
.
Most likely, these settings will not change often, and will probably be set once and that’s it. BUT, my perfectionist nature will be happier this way)) when everything works as it should.
Later, after testing, I will write a manual for everyone on how to completely connect this device with openhab.

Thanks again for putting me on the right path.

Nicely done.

Ok - That’s a valid ‘use case’ I definitely understand, along with ‘OTT’ (Over the top…) :slight_smile: - Both use cases appear in my automation frequently.
But sometimes the best learning comes from finding a hard way to solve an easy problem !!

Look forward to seeing your manual, once you complete it.

Cheers - Glen.

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.