Skip to main content

element_iot

Version: 1.1.0 (2025-08-27)

defmodule Parser do

# >>> Include platform-related modules here
use Platform.Parsing.Behaviour
# <<<

def parse(<<__r1::5, ch_temp_en::1, ch_vsys_en::1, usb_pwr::1, # META
__r2::7, ct_plus_mod::1, # GLOBAL
__r3::3, in1_v_mod::1, in1_scale_mod::1, in1_freq_en::1, in1_dc_en::1, in1_ac_en::1, # AIN
__r4::3, in2_v_mod::1, in2_scale_mod::1, in2_freq_en::1, in2_dc_en::1, in2_ac_en::1, # AIN
__r5::3, in3_v_mod::1, in3_scale_mod::1, in3_freq_en::1, in3_dc_en::1, in3_ac_en::1, # AIN
__r6::3, in4_v_mod::1, in4_scale_mod::1, in4_freq_en::1, in4_dc_en::1, in4_ac_en::1, # AIN
__r7::32, bytes::binary>>,
%{meta: %{frame_port: 10}}) do

### FLAG MAP
flags_map = %{
"ch_temp_en" => ch_temp_en, "ch_vsys_en" => ch_vsys_en, "usb_pwr" => usb_pwr,
"in1_v_mod" => in1_v_mod, "in1_scale_mod" => in1_scale_mod, "in1_freq_en" => in1_freq_en, "in1_dc_en" => in1_dc_en, "in1_ac_en" => in1_ac_en,
"in2_v_mod" =>in2_v_mod, "in2_scale_mod" => in2_scale_mod, "in2_freq_en" => in2_freq_en, "in2_dc_en" => in2_dc_en, "in2_ac_en" => in2_ac_en,
"in3_v_mod" => in3_v_mod, "in3_scale_mod" => in3_scale_mod, "in3_freq_en" => in3_freq_en, "in3_dc_en" => in3_dc_en, "in3_ac_en" => in3_ac_en,
"in4_v_mod" => in4_v_mod, "in4_scale_mod" => in4_scale_mod, "in4_freq_en" => in4_freq_en, "in4_dc_en" => in4_dc_en, "in4_ac_en" => in4_ac_en
}

### DATA MAP
rmap = %{}
rmap = Map.put(rmap, "index", 0)

rmap = extract_system_vars(rmap, ch_vsys_en, ch_temp_en, bytes)

rmap = Enum.reduce [1, 2, 3, 4], rmap, fn ch, rmap_in ->
extract_results_in(flags_map, rmap_in, ch, bytes)
end

rmap = sumIfDefined(flags_map, rmap, "_ac_A", "sum_in1234_ac_A")
rmap = sumIfDefined(flags_map, rmap, "_dc_A", "sum_in1234_dc_A")

rmap = extract_ct_plus(rmap, ct_plus_mod, bytes)

######################################################
rmap = Map.delete(rmap, "index")
rmap
end
##########################################################################################################################################################################################


defp default_config() do
%{
"register_full_1" => "1-0:1.8.0"
}
end

def use_default(), do: true # Oder true, je nach gewünschtem Modus


defp get_config(meta) do
profile_config = get(meta, [:device, :fields], nil)

klax_config =
Enum.map(default_config(), fn {k, _v} ->
{k, replace_config_values(use_default(), k, profile_config)}
end)
|> Enum.into(%{})

%{
register_full_1: klax_config["register_full_1"]
}
end

defp replace_config_values(false, key, klax_config),
do: get(klax_config, [key], Map.get(default_config(), key))

defp replace_config_values(true, key, _klax_config),
do: get(default_config(), [key])




def parse(
<<bitset_byte_0::8, bitset_byte_1::8, bitset_byte_2::8, in_ctr_count::little-integer-size(24),
_not_necessary::8, in1_power_en::little-signed-integer-size(32),
in1_cosphi_en::little-float-size(16), in2_power_en::little-signed-integer-size(32),
in2_cosphi_en::little-float-size(16), in3_power_en::little-signed-integer-size(32),
in3_cosphi_en::little-float-size(16), rest::binary>>,
%{meta: %{frame_port: 30}} = meta
) do

# Berechnung der Werte mit Skalierungsfaktoren
in1_kWh = Float.round(in1_power_en * 0.01, 2)
in1_cosphi = Float.round(in1_cosphi_en * 1.0, 2)

in2_kWh = Float.round(in2_power_en * 0.01, 2)
in2_cosphi = Float.round(in2_cosphi_en * 1.0, 2)

in3_kWh = Float.round(in3_power_en * 0.01, 2)
in3_cosphi = Float.round(in3_cosphi_en * 1.0, 2)

# Berechnung der Summe
sum_in_123_kWh = Float.round(in1_kWh + in2_kWh + in3_kWh, 2)

# Berechnung der Zeit aus in_ctr_count
days = div(in_ctr_count, 24 * 3600)
hours = div(rem(in_ctr_count, 24 * 3600), 3600)
minutes = div(rem(in_ctr_count, 3600), 60)
seconds = rem(in_ctr_count, 60)

# Darstellung des Zeitverlaufs
in_ctr_time_elapse = "[D:#{days}|H:#{hours}|M:#{minutes}|S:#{seconds}]"

# Holen des register_full_1 Werts aus der Konfiguration
klax_config = get_config(meta)
register_full_1 = klax_config[:register_full_1]


# Erste Map mit Atom-Schlüsseln
atom_key_map = %{
in1_kWh: in1_kWh,
in1_cosphi: in1_cosphi,
in2_kWh: in2_kWh,
in2_cosphi: in2_cosphi,
in3_kWh: in3_kWh,
in3_cosphi: in3_cosphi,
in_ctr_count: in_ctr_count,
sum_in_123_kWh: sum_in_123_kWh,
in_ctr_time_elapse: in_ctr_time_elapse
}

# Zweite Map mit dem dynamischen Schlüssel
dynamic_key_map = %{
register_full_1 => sum_in_123_kWh # Verwende den Zahlencode von register_full_1 als Schlüssel
}

# Kombiniere beide Maps
result_map = Map.merge(atom_key_map, dynamic_key_map)
end


##############################################################################################################################################################################################
def parse(payload, %{meta: %{frame_port: 6}}) do
%{message: "old message format, please contact your supplier for the decoder"}
end

def parse(<<bytes::binary>>, %{meta: %{frame_port: 99}}) do
%{message: "reboot counter"}
end

def parse(payload, meta) do
port = meta[:meta]
%{message: "invalid port", port: port[:frame_port]}
end

def extract_system_vars(rmap, ch_vsys_en, ch_temp_en, data) do
rmap = if ch_vsys_en do
index = rmap["index"]

vsys_raw = get_value(data, index, 2)
vsys = (vsys_raw * (3.7125-1.8) / 255.0) + 1.8

rmap = Map.put(rmap, "vsys_V", vsys)
rmap = Map.delete(rmap, "index")
rmap = Map.put(rmap, "index", index+1)
else
rmap
end
rmap = if ch_temp_en do
index = rmap["index"]

temp_C_raw = get_value(data, index, 2)
temp_C = (temp_C_raw) * (80 - (-22)) / 255 - 22

rmap = Map.put(rmap, "temp_C", temp_C)
rmap = Map.delete(rmap, "index")
rmap = Map.put(rmap, "index", index+1)
else
rmap
end
end


def sumIfDefined(flags_map, rmap, suffix, sumKey) do

key1 = "in1#{suffix}"
key2 = "in2#{suffix}"
key3 = "in3#{suffix}"
key4 = "in4#{suffix}"

rmap = if ((rmap[key1]!=nil) || (rmap[key2]!=nil) || (rmap[key3]!=nil) || (rmap[key4]!=nil)) do
newSum = 0
## Key 1
newSum = if (rmap[key1] != nil) do
newSum = newSum + rmap[key1]
else
newSum
end
## Key 2
newSum = if (rmap[key2] != nil) do
newSum = newSum + rmap[key2]
else
newSum
end
## Key 3
newSum = if (rmap[key3] != nil) do
newSum = newSum + rmap[key3]
else
newSum
end
## Key 4
newSum = if (rmap[key4] != nil) do
newSum = newSum + rmap[key4]
else
newSum
end
rmap = Map.put(rmap, sumKey, newSum)
else
rmap
end
rmap
end

def extract_ct_plus(rmap, ct_plus_mod, data) do
error = 0

rmap = if (ct_plus_mod == 1) do
#### TOTAL
rmap = Enum.reduce [1, 2, 3], rmap, fn ch, rmap_pwr_coeff ->
in_pow_factor = get_value(data, rmap["index"]+(ch-1)*2, 0)
rmap = Map.put(rmap_pwr_coeff, "in#{ch}_pow_factor", in_pow_factor)
end

#### Calculate Power VA ####
rmap = if (rmap["in4_voltage_VAC"] != nil) do
rmap = if (rmap["in1_ac_A"] != nil) do
rmap = Map.put(rmap, "in1_pow_app_VA", rmap["in1_ac_A"] * rmap["in4_voltage_VAC"])
rmap = Map.put(rmap, "in1_pow_act_W", rmap["in1_pow_app_VA"] * rmap["in1_pow_factor"])
rmap = Map.put(rmap, "in1_pow_react_VAR", :math.sqrt( abs( (rmap["in1_pow_app_VA"] * rmap["in1_pow_app_VA"]) - (rmap["in1_pow_act_W"] * rmap["in1_pow_act_W"]) ) ))
else
rmap = Map.put(rmap, "in1_pow_app_VA", error)
rmap = Map.put(rmap, "in1_pow_act_W", error)
rmap = Map.put(rmap, "in1_pow_react_VAR", error)
end

rmap = if (rmap["in2_ac_A"] != nil) do
rmap = Map.put(rmap, "in2_pow_app_VA", rmap["in2_ac_A"] * rmap["in4_voltage_VAC"])
rmap = Map.put(rmap, "in2_pow_act_W", rmap["in2_pow_app_VA"] * rmap["in2_pow_factor"])
rmap = Map.put(rmap, "in2_pow_react_VAR", :math.sqrt(abs((rmap["in2_pow_app_VA"] * rmap["in2_pow_app_VA"]) - (rmap["in2_pow_act_W"] * rmap["in2_pow_act_W"]))))
else
rmap = Map.put(rmap, "in2_pow_app_VA", error)
rmap = Map.put(rmap, "in2_pow_act_W", error)
rmap = Map.put(rmap, "in2_pow_react_VAR", error)
end

rmap = if (rmap["in3_ac_A"] != nil) do
rmap = Map.put(rmap, "in3_pow_app_VA", rmap["in3_ac_A"] * rmap["in4_voltage_VAC"])
rmap = Map.put(rmap, "in3_pow_act_W", rmap["in3_pow_app_VA"] * rmap["in3_pow_factor"])
rmap = Map.put(rmap, "in3_pow_react_VAR", :math.sqrt(abs((rmap["in3_pow_app_VA"] * rmap["in3_pow_app_VA"]) - (rmap["in3_pow_act_W"] * rmap["in3_pow_act_W"]))))
else
rmap = Map.put(rmap, "in3_pow_app_VA", error)
rmap = Map.put(rmap, "in3_pow_act_W", error)
rmap = Map.put(rmap, "in3_pow_react_VAR", error)
end

### SUMS
rmap = if ((rmap["in1_ac_A"] != nil) && (rmap["in2_ac_A"] != nil) && (rmap["in3_ac_A"] != nil)) do
rmap = Map.put(rmap, "sum_in123_pow_app_VA", rmap["in1_pow_app_VA"]+rmap["in2_pow_app_VA"]+rmap["in3_pow_app_VA"])
rmap = Map.put(rmap, "sum_in123_pow_act_W", rmap["in1_pow_act_W"]+rmap["in2_pow_act_W"]+rmap["in3_pow_act_W"])
rmap = Map.put(rmap, "sum_in123_pow_react_VAR",rmap["in1_pow_react_VAR"]+rmap["in2_pow_react_VAR"]+rmap["in3_pow_react_VAR"])
else
rmap = Map.put(rmap, "sum_in123_pow_app_VA", error)
rmap = Map.put(rmap, "sum_in123_pow_act_W", error)
rmap = Map.put(rmap, "sum_in123_pow_react_VAR", error)
end

else
rmap = Map.put(rmap, "in1_pow_app_VA", error)
rmap = Map.put(rmap, "in2_pow_app_VA", error)
rmap = Map.put(rmap, "in3_pow_app_VA", error)

#### Calculate Power W ####
rmap = Map.put(rmap, "in1_pow_act_W", error)
rmap = Map.put(rmap, "in2_pow_act_W", error)
rmap = Map.put(rmap, "in3_pow_act_W", error)

#### Calculate VAR power
rmap = Map.put(rmap, "in1_pow_react_VAR", error)
rmap = Map.put(rmap, "in2_pow_react_VAR", error)
rmap = Map.put(rmap, "in3_pow_react_VAR", error)

### SUMS
rmap = Map.put(rmap, "sum_in123_pow_app_VA", error)
rmap = Map.put(rmap, "sum_in123_pow_act_W", error)
rmap = Map.put(rmap, "sum_in123_pow_react_VAR", error)
end
else
rmap
end
end

def extract_results_in(flags_map, rmap, ch, data) do
################################################################
##### AC EN
rmap = if (flags_map["in#{ch}_ac_en"]==1) do
index = rmap["index"]
in_ac_raw_A = get_value(data, index, 0)
rmap = Map.put(rmap, "in#{ch}_ac_raw_A", in_ac_raw_A)
rmap = Map.delete(rmap, "index")
rmap = Map.put(rmap, "index", index+2)
else
rmap
end
##### DC EN
rmap = if (flags_map["in#{ch}_dc_en"]==1) do
index = rmap["index"]
in_dc_raw_A = get_value(data, index, 0)
rmap = Map.put(rmap, "in#{ch}_dc_raw_A", in_dc_raw_A)
rmap = Map.delete(rmap, "index")
rmap = Map.put(rmap, "index", index+2)
else
rmap
end
##### FREQ EN
rmap = if (flags_map["in#{ch}_freq_en"]==1) do
index = rmap["index"]
in_freq = get_value(data, index, 1)/100
rmap = Map.put(rmap, "in#{ch}_freq", in_freq)
rmap = Map.delete(rmap, "index")
rmap = Map.put(rmap, "index", index+2)
else
rmap
end
##### COEFF EN
rmap = if (flags_map["in#{ch}_scale_mod"]==1) do
index = rmap["index"]

in_coeff = get_value(data, index, 0)
rmap = Map.put(rmap, "in#{ch}_coeff", in_coeff)
rmap = Map.delete(rmap, "index")
rmap = Map.put(rmap, "index", index+2)

## Voltage mode
in_v_mod = flags_map["in#{ch}_v_mod"]
in_coeff = if (in_coeff==0) do 1 else in_coeff end
in_coeff = if (in_v_mod==1) do in_coeff * 1000 else in_coeff end

## AC
rmap = if (flags_map["in#{ch}_ac_en"] == 1) do
in_ac_raw_A = rmap["in#{ch}_ac_raw_A"]
key_add = if (in_v_mod==1) do
"in#{ch}_voltage_VAC"
else
"in#{ch}_ac_A"
end
Map.put(rmap, key_add, in_ac_raw_A * in_coeff)
end

## DC
rmap = if (flags_map["in#{ch}_dc_en"] == 1) do
in_dc_raw_A = rmap["in#{ch}_dc_raw_A"]
key_add = if (in_v_mod==1) do
"in#{ch}_voltage_VDC"
else
"in#{ch}_dc_A"
end
Map.put(rmap, key_add, in_dc_raw_A * in_coeff)
else
rmap
end
else
rmap
end
rmap
end

def get_value(binary_arr, index, type) do
cond do
(type == 0) ->
<<front::binary-size(index), bin_value::binary-size(2), rest::binary>> = binary_arr
get_fp16(bin_value)
(type == 1) ->
<<front::binary-size(index), bin_value::binary-size(2), rest::binary>> = binary_arr
<<return_val::little-unsigned-16>> = bin_value
return_val
(type == 2) ->
<<front::binary-size(index), bin_value::binary-size(1), rest::binary>> = binary_arr
<<return_val::little-unsigned-8>> = bin_value
return_val
(type == 3) ->
<<front::binary-size(index), bin_value::binary-size(4), rest::binary>> = binary_arr
<<return_val::little-unsigned-32>> = bin_value
return_val
(type == 4) ->
<<front::binary-size(index), bin_value::binary-size(2), rest::binary>> = binary_arr
<<return_val::little-signed-16>> = bin_value
return_val
(type == 5) ->
<<front::binary-size(index), bin_value::binary-size(4), rest::binary>> = binary_arr
<<return_val::little-signed-32>> = bin_value
return_val
end
end

def get_fp16(<<f2::8, s::1, e::5, f1::2>>) do
<<f::10>> = <<f1::2, f2::8>>
cond do
(e == 0) ->
cond do
(s==1) ->
-1 * 0.00006103515625 * (f / 1024.0)
(s==0) ->
1 * 0.00006103515625 * (f / 1024.0)
end
(e == 31) ->
cond do
(f > 0) ->
nil
(f == 0) ->
cond do
(s == 1) ->
:negative_infinity
(s == 0) ->
:positive_infinity
end
end
(true) ->
cond do
(s == 1) ->
s = -1 * (:math.pow(2.0, (e - 15))) * (1 + f / 1024.0)
(s == 0) ->
s = 1 * (:math.pow(2.0, (e - 15))) * (1 + f / 1024.0)
end
end
end



def fields() do
[
%{
field: "vsys_V",
display: "system voltage",
unit: "V"
},
%{
field: "temp_C",
display: "temperature",
unit: "°C"
},
%{
field: "in1_ac_raw_A",
display: "in1 ac current",
unit: "A"
},
%{
field: "in2_ac_raw_A",
display: "in2 ac current",
unit: "A"
},
%{
field: "in3_ac_raw_A",
display: "in3 ac current",
unit: "A"
},
%{
field: "in4_ac_raw_A",
display: "in4 ac current",
unit: "A"
},
%{
field: "in1_dc_raw_A",
display: "in1 dc current",
unit: "A"
},
%{
field: "in2_dc_raw_A",
display: "in2 dc current",
unit: "A"
},
%{
field: "in3_dc_raw_A",
display: "in3 dc current",
unit: "A"
},
%{
field: "in4_dc_raw_A",
display: "in4 dc current",
unit: "A"
},
%{
field: "in1_ac_A",
display: "in1 ac current",
unit: "A"
},
%{
field: "in2_ac_A",
display: "in2 ac current",
unit: "A"
},
%{
field: "in3_ac_A",
display: "in3 ac current",
unit: "A"
},
%{
field: "in4_ac_A",
display: "in4 ac current",
unit: "A"
},
%{
field: "in1_dc_A",
display: "in1 dc current",
unit: "A"
},
%{
field: "in2_dc_A",
display: "in2 dc current",
unit: "A"
},
%{
field: "in3_dc_A",
display: "in3 dc current",
unit: "A"
},
%{
field: "in4_dc_A",
display: "in4 dc current",
unit: "A"
},
%{
field: "in1_freq",
display: "in1 grid frequency",
unit: "Hz"
},
%{
field: "in2_freq",
display: "in2 grid frequency",
unit: "Hz"
},
%{
field: "in3_freq",
display: "in3 grid frequency",
unit: "Hz"
},
%{
field: "in4_freq",
display: "in4 grid frequency",
unit: "Hz"
},
%{
field: "in1_coeff",
display: "in1 coefficient",
unit: "_"
},
%{
field: "in2_coeff",
display: "in2 coefficient",
unit: "_"
},
%{
field: "in3_coeff",
display: "in3 coefficient",
unit: "_"
},
%{
field: "in4_coeff",
display: "in4 coefficient",
unit: "_"
},
%{
field: "in1_pow_factor",
display: "in1 cosphi",
},
%{
field: "in2_pow_factor",
display: "in2 cosphi",
},
%{
field: "in3_pow_factor",
display: "in3 cosphi",
},
%{
field: "in1_pow_act_W",
display: "in1 real power",
unit: "W"
},
%{
field: "in2_pow_act_W",
display: "in2 real power",
unit: "W"
},
%{
field: "in3_pow_act_W",
display: "in3 real power",
unit: "W"
},
%{
field: "in1_pow_app_VA",
display: "in1 apparent power",
unit: "VA"
},
%{
field: "in2_pow_app_VA",
display: "in2 apparent power",
unit: "VA"
},
%{
field: "in3_pow_app_VA",
display: "in3 apparent power",
unit: "VA"
},
%{
field: "in1_pow_react_VAR",
display: "in1 reactive power",
unit: "VAR"
},
%{
field: "in2_pow_react_VAR",
display: "in2 reactive power",
unit: "VAR"
},
%{
field: "in3_pow_react_VAR",
display: "in3 reactive power",
unit: "VAR"
},
%{
field: "pow_sum_In123_W",
display: "Summed reactive power",
unit: "W"
},
%{
field: "sum_in1234_ac_A",
display: "Summed AC input",
unit: "A"
},
%{
field: "sum_in1234_dc_A",
display: "Summed DC input",
unit: "A"
},
%{
field: "sum_in123_pow_app_VA",
display: "Summed apparent power",
unit: "VA"
},
%{
field: "sum_in123_pow_act_W",
display: "Summed real power",
unit: "W"
},
%{
field: "sum_in123_pow_react_VAR",
display: "Summed reactive power",
unit: "VAR"
},
%{
field: "reboot_counter",
display: "Reboot Counter",
unit: "_"
},
%{
field: "in1_kWh",
display: "Input 1 Energy",
unit: "kWh"
},
%{
field: "in1_cosphi",
display: "Input 1 Power Factor",
unit: "_"
},
%{
field: "in2_kWh",
display: "Input 2 Energy",
unit: "kWh"
},
%{
field: "in2_cosphi",
display: "Input 2 Power Factor",
unit: "_"
},
%{
field: "in3_kWh",
display: "Input 3 Energy",
unit: "kWh"
},
%{
field: "in3_cosphi",
display: "Input 3 Power Factor",
unit: "_"
},
%{
field: "sum_in_123_kWh",
display: "Sum of Inputs 1-3 Energy",
unit: "kWh"
},
%{
field: "in_ctr_time_elapse",
display: "Time Elapsed",
unit: "_"
},
%{
field: "1-0:1.8.0",
display: "1-0:1.8.0"
}
]
end
end