');
if (gad.whitelist === 'false') $('.permission').hide();
@@ -316,6 +326,7 @@ function sveGADEdtorAddTypeSelect(device, gadName, gad) {
$('#gadeditor').append('
' + 'mode' + '
');
$('').val('item').text('item').appendTo('#gadEditTypeSelect');
$('').val('plot').text('plot').appendTo('#gadEditTypeSelect');
+ $('').val('log').text('log').appendTo('#gadEditTypeSelect');
$('#gadEditTypeSelect').val(gad.editor);
}
From 975b6effec75f53d3d79eaca3618bdcceb807cbb Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Thu, 8 Sep 2022 19:18:56 +0200
Subject: [PATCH 02/19] Update 31_fronthemDevice.pm
typo Log3
Change herrmanj 29.4.18
---
FHEM/31_fronthemDevice.pm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/FHEM/31_fronthemDevice.pm b/FHEM/31_fronthemDevice.pm
index de67da8..893a958 100644
--- a/FHEM/31_fronthemDevice.pm
+++ b/FHEM/31_fronthemDevice.pm
@@ -145,7 +145,7 @@ fronthemDevice_Get(@)
1;
} or do {
my $e = $@;
- log3 ($hash->{NAME}, 1, "Error decoding webif-data $e: ".join(' ',@args));
+ Log3 ($hash->{NAME}, 1, "Error decoding webif-data $e: ".join(' ',@args));
return undef;
};
From e17d3d28d5a10eaf9ec768893af307e28c58417a Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Thu, 8 Sep 2022 21:15:18 +0200
Subject: [PATCH 03/19] Update 99_fronthemUtils.pm
allow negative values in plots
https://forum.fhem.de/index.php?topic=94990.msg877864#msg877864
---
FHEM/99_fronthemUtils.pm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/FHEM/99_fronthemUtils.pm b/FHEM/99_fronthemUtils.pm
index bd520f3..c12858a 100644
--- a/FHEM/99_fronthemUtils.pm
+++ b/FHEM/99_fronthemUtils.pm
@@ -771,7 +771,7 @@ sub Plotfile(@)
pop @resonse;
for (my $i = 0; $i < @resonse; $i++) {
- $resonse[$i] =~ /([0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}:[0-9]{2})\s+([0-9\.]+)/;
+ $resonse[$i] =~ /([0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}:[0-9]{2})\s+([0-9\.-]+)/;
push(@{$data[0]->{plotdata}[$i]}, main::fronthem_TimeStamp($1));
push(@{$data[0]->{plotdata}[$i]}, sprintf("%#.4f", $2) * 1);
}
From 527e5bf4d785aa348999aaab48280ecb2021f0f5 Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Thu, 8 Sep 2022 21:31:21 +0200
Subject: [PATCH 04/19] Update 99_fronthemUtils.pm
fix log spamming issue
https://forum.fhem.de/index.php/topic,86584.msg1111928.html#msg1111928
---
FHEM/99_fronthemUtils.pm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/FHEM/99_fronthemUtils.pm b/FHEM/99_fronthemUtils.pm
index c12858a..d095375 100644
--- a/FHEM/99_fronthemUtils.pm
+++ b/FHEM/99_fronthemUtils.pm
@@ -655,7 +655,7 @@ sub Plot(@)
$duration = main::fronthem_Duration($start);
}
- my $string = main::fhem('get ' . $args[0] . ' - webchart ' . $from . ' ' . $to . ' ' . $device . ' ' . $duration . ' TIMESTAMP ' . $reading);
+ my $string = main::CommandGet(undef, $args[0] . ' - webchart ' . $from . ' ' . $to . ' ' . $device . ' ' . $duration . ' TIMESTAMP ' . $reading);
my @resonse = main::fronthem_decodejson($string);
foreach my $data (@resonse) {
From 0186348adb8ade508b637e5bcf2bc0157267ef4c Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Fri, 9 Sep 2022 23:35:01 +0200
Subject: [PATCH 05/19] Version info
---
CHANGED | 3 +++
controls_fronthem.txt | 7 ++++---
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/CHANGED b/CHANGED
index 4d75599..5fd58ac 100644
--- a/CHANGED
+++ b/CHANGED
@@ -1,3 +1,6 @@
+2022-09-08
+- Adaptions to smartVISU v2.9+ from 2018: thx raman
+
2017-12-15
- Modified state handling in received events: thx ddtlabs
diff --git a/controls_fronthem.txt b/controls_fronthem.txt
index 2b77c35..afcbeb7 100644
--- a/controls_fronthem.txt
+++ b/controls_fronthem.txt
@@ -1,8 +1,9 @@
-UPD 2021-03-21_10:06:23 21901 FHEM/01_fronthem.pm
-UPD 2021-03-21_10:06:09 21015 FHEM/31_fronthemDevice.pm
+UPD 2022-09-08_18:54:48 27037 FHEM/01_fronthem.pm
+UPD 2022-09-08_19:16:02 28635 FHEM/31_fronthemDevice.pm
+UPD 2022-09-08_21:30:22 21580 FHEM/99_fronthemUtils.pm
UPD 2015-02-13_21:23:35 7435 FHEM/fhwebsocket.pm
UPD 2019-01-03_11:22:09 9811 FHEM/fhconverter.pm
-UPD 2017-03-16_14:28:59 13825 www/pgm2/fronthemEditor.js
+UPD 2022-09-08_18:55:13 14158 www/pgm2/fronthemEditor.js
UPD 2015-01-16_11:51:34 462 www/images/default/arrow-down.svg
UPD 2015-01-16_11:51:34 464 www/images/default/arrow-up.svg
UPD 2015-01-16_11:51:34 706 www/images/default/desktop.svg
From 9c7b927f952af9e7b0eb58a76aa7c38a0efb5898 Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Sat, 19 Nov 2022 18:56:05 +0100
Subject: [PATCH 06/19] workaround for sporadic fhem crashesfhem
---
FHEM/31_fronthemDevice.pm | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/FHEM/31_fronthemDevice.pm b/FHEM/31_fronthemDevice.pm
index 893a958..3e87854 100644
--- a/FHEM/31_fronthemDevice.pm
+++ b/FHEM/31_fronthemDevice.pm
@@ -629,7 +629,18 @@ fronthemDevice_DoConverter(@)
my $msg;
$msg->{receiver} = $hash->{NAME};
$msg->{message}->{cmd} = $cmd;
- @{$msg->{message}->{items}} = @{$param->{gads}}?@{$param->{gads}}:($param->{gad}, $param->{gadval});
+# @{$msg->{message}->{items}} = @{$param->{gads}}?@{$param->{gads}}:($param->{gad}, $param->{gadval});
+#
+# Workaround because statement above throws errors if parameters are missing
+#
+ eval {
+ @{$msg->{message}->{items}} = @{$param->{gads}}?@{$param->{gads}}:($param->{gad}, $param->{gadval});
+ } or do {
+ use Data::Dumper;
+ print "Unexpected error due to missing gad parameter:";
+ print Dumper $msg;
+ return;
+ };
fronthemDevice_toDriver($hash, $msg);
return undef;
}
From f4a63bc3ab7c57a97f04fecd9f23e3c5fab6b9a4 Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Fri, 20 Jan 2023 16:20:59 +0100
Subject: [PATCH 07/19] introduce multi-term durations w/ 4 digits
---
FHEM/99_fronthemUtils.pm | 80 ++++++++++++++++++++++------------------
1 file changed, 44 insertions(+), 36 deletions(-)
diff --git a/FHEM/99_fronthemUtils.pm b/FHEM/99_fronthemUtils.pm
index d095375..36aabcb 100644
--- a/FHEM/99_fronthemUtils.pm
+++ b/FHEM/99_fronthemUtils.pm
@@ -1,6 +1,7 @@
##############################################
# $Id: 99_fronthemUtils.pm 0 2015-11-10 08:00:00Z herrmannj $
# modified: 2018-04-14 00:00:00Z raman
+# modified: 2022-11-27 11:00:00Z alkazaa and wvhn
package main;
use strict;
@@ -39,51 +40,58 @@ fronthem_TimeStamp($)
return $time * 1000;
}
+# evaluates smartVISU duration format with up to 4 digits (instead of 2)
+# loops through the parameter with more terms e.g. "1y 3m 5d 10h" and sum the results up
sub
fronthem_Time($$)
{
my ($time, $period) = @_;
- if ($period =~ /^(\d{1,2})(s|i|h|d|w|m|y)/)
+ my @periods = split(' ', $period); # split parameters like "1y 3m 5d 10h" into an array like ("1y","3m","5d","10h")
+ foreach my $period (@periods) # loop over the individual array elements
{
- my $newTime = 0;
- if ($2 eq "s")
+ if ($period =~ /^(\d{1,4})(s|i|h|d|w|m|y)/)
{
- $newTime = $1;
- }
- elsif ($2 eq "i")
- {
- $newTime = $1 * 60;
- }
- elsif ($2 eq "h")
- {
- $newTime = $1 * 3600;
- }
- elsif ($2 eq "d")
- {
- $newTime = $1 * 3600 * 24;
- }
- elsif ($2 eq "w")
- {
- $newTime = $1 * 3600 * 24 * 7;
- }
- elsif ($2 eq "m")
- {
- $newTime = $1 * 3600 * 24 * 30;
- }
- elsif ($2 eq "y")
- {
- $newTime = $1 * 3600 * 24 * 365;
+ my $newTime = 0;
+ if ($2 eq "s")
+ {
+ $newTime = $1;
+ }
+ elsif ($2 eq "i")
+ {
+ $newTime = $1 * 60;
+ }
+ elsif ($2 eq "h")
+ {
+ $newTime = $1 * 3600;
+ }
+ elsif ($2 eq "d")
+ {
+ $newTime = $1 * 3600 * 24;
+ }
+ elsif ($2 eq "w")
+ {
+ $newTime = $1 * 3600 * 24 * 7;
+ }
+ elsif ($2 eq "m")
+ {
+ $newTime = $1 * 3600 * 24 * 30;
+ }
+ elsif ($2 eq "y")
+ {
+ $newTime = $1 * 3600 * 24 * 365;
+ }
+ $time = $time - $newTime;
}
- $time = $time - $newTime;
}
return $time;
}
+# select the database evaluation mode from first term of smartVISU duration, e.g. "0d" is daystats but "1d" is hourstats
sub
fronthem_Duration($) # hourstats daystats weekstats monthstats yearstats
{
my ($duration) = @_;
- if ($duration =~ /^(\d{1,2})(s|i|h|d|w|m|y)/)
+ if ($duration =~ /^(\d{1,4})(s|i|h|d|w|m|y)/)
{
if ($2 eq "s" || $2 eq "i")
{
@@ -656,9 +664,9 @@ sub Plot(@)
}
my $string = main::CommandGet(undef, $args[0] . ' - webchart ' . $from . ' ' . $to . ' ' . $device . ' ' . $duration . ' TIMESTAMP ' . $reading);
- my @resonse = main::fronthem_decodejson($string);
+ my @response = main::fronthem_decodejson($string);
- foreach my $data (@resonse) {
+ foreach my $data (@response) {
my $i = 0;
foreach my $row (@{$data->{data}}) {
if ($mode eq "raw") { # [TIMESTAMP,VALUE]
@@ -767,11 +775,11 @@ sub Plotfile(@)
$to =~ s/ /_/ig;
my $string = main::fhem("get " . $args[0] . " - - " . $from . " " . $to ." " . $column .":" . $reading . ":0:" . $regex, 1);
- my @resonse = split("\n", $string);
- pop @resonse;
+ my @response = split("\n", $string);
+ pop @response;
- for (my $i = 0; $i < @resonse; $i++) {
- $resonse[$i] =~ /([0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}:[0-9]{2})\s+([0-9\.-]+)/;
+ for (my $i = 0; $i < @response; $i++) {
+ $response[$i] =~ /([0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}:[0-9]{2}:[0-9]{2})\s+([0-9\.-]+)/;
push(@{$data[0]->{plotdata}[$i]}, main::fronthem_TimeStamp($1));
push(@{$data[0]->{plotdata}[$i]}, sprintf("%#.4f", $2) * 1);
}
From 1b04e0a610ee6e7fee7b0fbcf9d67f26740f3eaf Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Fri, 20 Jan 2023 19:33:48 +0100
Subject: [PATCH 08/19] allow absolute time values in plots
---
FHEM/99_fronthemUtils.pm | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/FHEM/99_fronthemUtils.pm b/FHEM/99_fronthemUtils.pm
index 36aabcb..e5ae5e4 100644
--- a/FHEM/99_fronthemUtils.pm
+++ b/FHEM/99_fronthemUtils.pm
@@ -42,10 +42,20 @@ fronthem_TimeStamp($)
# evaluates smartVISU duration format with up to 4 digits (instead of 2)
# loops through the parameter with more terms e.g. "1y 3m 5d 10h" and sum the results up
+# Bonus: a leading term with 0 like e.g. "0w" will not contribute to the total time but it can
+# be used to select the aggregation period for aggregation modes 'avg', 'min', 'sum' etc. in sub fronthem_Duration
sub
fronthem_Time($$)
{
my ($time, $period) = @_;
+
+ # allow Unix timestamp in milliseconds as well as "now"
+ if ($period =~ /^(.*?)\s*(\d{13})/) {
+ return int($2/1000 + 0.5);
+ }
+ if ($period eq "now") {
+ return $time;
+
my @periods = split(' ', $period); # split parameters like "1y 3m 5d 10h" into an array like ("1y","3m","5d","10h")
foreach my $period (@periods) # loop over the individual array elements
{
From 0ee89b9c9b1288c59991b1487c6293ca0b5b1fcf Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Sun, 12 Feb 2023 14:10:16 +0100
Subject: [PATCH 09/19] fix missing bracket
---
FHEM/99_fronthemUtils.pm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/FHEM/99_fronthemUtils.pm b/FHEM/99_fronthemUtils.pm
index e5ae5e4..c66c0f8 100644
--- a/FHEM/99_fronthemUtils.pm
+++ b/FHEM/99_fronthemUtils.pm
@@ -55,7 +55,7 @@ fronthem_Time($$)
}
if ($period eq "now") {
return $time;
-
+ }
my @periods = split(' ', $period); # split parameters like "1y 3m 5d 10h" into an array like ("1y","3m","5d","10h")
foreach my $period (@periods) # loop over the individual array elements
{
From 35e7aaa9b83c39ed51542be9c62e090f5f1b91db Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Fri, 8 Dec 2023 16:51:17 +0100
Subject: [PATCH 10/19] allow negative durations for plots
---
FHEM/99_fronthemUtils.pm | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/FHEM/99_fronthemUtils.pm b/FHEM/99_fronthemUtils.pm
index c66c0f8..abdd4e8 100644
--- a/FHEM/99_fronthemUtils.pm
+++ b/FHEM/99_fronthemUtils.pm
@@ -59,7 +59,7 @@ fronthem_Time($$)
my @periods = split(' ', $period); # split parameters like "1y 3m 5d 10h" into an array like ("1y","3m","5d","10h")
foreach my $period (@periods) # loop over the individual array elements
{
- if ($period =~ /^(\d{1,4})(s|i|h|d|w|m|y)/)
+ if ($period =~ /^([-+]?\d{1,4})(s|i|h|d|w|m|y)/)
{
my $newTime = 0;
if ($2 eq "s")
@@ -90,7 +90,7 @@ fronthem_Time($$)
{
$newTime = $1 * 3600 * 24 * 365;
}
- $time = $time - $newTime;
+ $time -= $newTime;
}
}
return $time;
From 3bc13fd90d039b0381135bb25a0dda604ad37758 Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Tue, 17 Feb 2026 11:57:21 +0100
Subject: [PATCH 11/19] fix comment for UZSU converter
---
FHEM/99_fronthemUtils.pm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/FHEM/99_fronthemUtils.pm b/FHEM/99_fronthemUtils.pm
index abdd4e8..9fc2149 100644
--- a/FHEM/99_fronthemUtils.pm
+++ b/FHEM/99_fronthemUtils.pm
@@ -409,7 +409,7 @@ use warnings;
###############################################################################
# For use with UZSU-Widget in SV and UZSU-notify in fhem
# Setreading a device reading using JSON conversion (gadval => reading=decode_json() => setval => encode_json(reading) )
-# the reading ("uzsu") must be created manually for each UZSU-enabled device in fhem using "setreading uzsu {}"
+# the reading ("uzsu") must be created manually for each UZSU-enabled device in fhem using "setreading uzsu {"active:false,"list":[]}
# in the fhem commandline
###############################################################################
From b75a243cd2974f03b87b1730b12d3485735bfecb Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Wed, 18 Feb 2026 16:54:53 +0100
Subject: [PATCH 12/19] Update controls_fronthem.txt
---
controls_fronthem.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/controls_fronthem.txt b/controls_fronthem.txt
index afcbeb7..75d3e6a 100644
--- a/controls_fronthem.txt
+++ b/controls_fronthem.txt
@@ -1,6 +1,6 @@
UPD 2022-09-08_18:54:48 27037 FHEM/01_fronthem.pm
-UPD 2022-09-08_19:16:02 28635 FHEM/31_fronthemDevice.pm
-UPD 2022-09-08_21:30:22 21580 FHEM/99_fronthemUtils.pm
+UPD 2022-09-08_19:16:02 28957 FHEM/31_fronthemDevice.pm
+UPD 2026-02-17_21:30:22 22559 FHEM/99_fronthemUtils.pm
UPD 2015-02-13_21:23:35 7435 FHEM/fhwebsocket.pm
UPD 2019-01-03_11:22:09 9811 FHEM/fhconverter.pm
UPD 2022-09-08_18:55:13 14158 www/pgm2/fronthemEditor.js
From 5845864a642a92d723ab4b434c61372cfe63afde Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Thu, 19 Feb 2026 09:33:43 +0100
Subject: [PATCH 13/19] Update CHANGED
---
CHANGED | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/CHANGED b/CHANGED
index 5fd58ac..1740f1c 100644
--- a/CHANGED
+++ b/CHANGED
@@ -1,3 +1,9 @@
+2023-12-18
+- extended plot durations to allow usage of all smartVISU plot features
+
+2022-11-19
+- workaround for sporadic fhem crashes
+
2022-09-08
- Adaptions to smartVISU v2.9+ from 2018: thx raman
From d22270a4d47ca83797257e9e4904f0e13c5f2a0d Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Thu, 19 Feb 2026 13:51:41 +0100
Subject: [PATCH 14/19] add sunrise/sunset to UZSU Dict
+ improve documentation
+ reformat file
---
FHEM/99_fronthemUtils.pm | 239 ++++++++++++++++++++++++++-------------
controls_fronthem.txt | 2 +-
2 files changed, 163 insertions(+), 78 deletions(-)
diff --git a/FHEM/99_fronthemUtils.pm b/FHEM/99_fronthemUtils.pm
index 9fc2149..9335d24 100644
--- a/FHEM/99_fronthemUtils.pm
+++ b/FHEM/99_fronthemUtils.pm
@@ -41,23 +41,23 @@ fronthem_TimeStamp($)
}
# evaluates smartVISU duration format with up to 4 digits (instead of 2)
-# loops through the parameter with more terms e.g. "1y 3m 5d 10h" and sum the results up
+# loops through the parameter with more terms e.g. "1y 3m 5d 10h" and sums the results up
# Bonus: a leading term with 0 like e.g. "0w" will not contribute to the total time but it can
# be used to select the aggregation period for aggregation modes 'avg', 'min', 'sum' etc. in sub fronthem_Duration
sub
fronthem_Time($$)
{
my ($time, $period) = @_;
-
+
# allow Unix timestamp in milliseconds as well as "now"
- if ($period =~ /^(.*?)\s*(\d{13})/) {
+ if ($period =~ /^(.*?)\s*(\d{13})/) {
return int($2/1000 + 0.5);
}
if ($period eq "now") {
return $time;
- }
+ }
my @periods = split(' ', $period); # split parameters like "1y 3m 5d 10h" into an array like ("1y","3m","5d","10h")
- foreach my $period (@periods) # loop over the individual array elements
+ foreach my $period (@periods) # and loop over the array elements
{
if ($period =~ /^([-+]?\d{1,4})(s|i|h|d|w|m|y)/)
{
@@ -97,8 +97,9 @@ fronthem_Time($$)
}
# select the database evaluation mode from first term of smartVISU duration, e.g. "0d" is daystats but "1d" is hourstats
+# available: timerange hourstats daystats weekstats monthstats yearstats
sub
-fronthem_Duration($) # hourstats daystats weekstats monthstats yearstats
+fronthem_Duration($)
{
my ($duration) = @_;
if ($duration =~ /^(\d{1,4})(s|i|h|d|w|m|y)/)
@@ -146,6 +147,16 @@ fronthem_Duration($) # hourstats daystats weekstats monthstats yearstats
return "timerange";
}
+sub fronthem_sunrise($) {
+ my ($hour,$min,$sec) = split(/:/, sunrise_abs($_[0]));
+ return $hour . ':' . $min;
+}
+
+sub fronthem_sunset($) {
+ my ($hour,$min,$sec) = split(/:/, sunset_abs($_[0]));
+ return $hour . ':' . $min;
+}
+
###############################################################################
#
# Umsetzen der UZSU-Settings für ein device
@@ -160,45 +171,45 @@ fronthem_Duration($) # hourstats daystats weekstats monthstats yearstats
#
# define UZSU notify .*:uzsu:.* { UZSU_execute($NAME, $EVTPART1, 'save') }
#
-# und folgendes Attribut setzen:
+# Damit die Einstellungen gespeichert werden können, folgendes Attribut setzen:
+# attr global autosave 1
#
-# attr global autosave 1
#
sub UZSU_execute($$;$)
{
- my ($device, $uzsu, $save) = @_;
+ my ($device, $uzsu, $save) = @_;
$uzsu = decode_json($uzsu);
- fhem('delete wdt_uzsu_'.$device.'.*');
+ fhem('delete wdt_uzsu_'.$device.'.*');
- for (my $i = 0; $i < @{$uzsu->{list}}; $i++) {
- if ($uzsu->{list}[$i]->{active}) {
- my %rrule = UZSU_getRrules($uzsu->{list}[$i]{rrule});
- my $holiday = $uzsu->{list}[$i]{holiday}{weekend} && $uzsu->{list}[$i]{holiday}{workday} ? '' : $uzsu->{list}[$i]{holiday}{weekend} ? $rrule{'BYDAY'} ne '' ? ',$we' : '$we' : $uzsu->{list}[$i]{holiday}{workday} ? $rrule{'BYDAY'} ne '' ? ',!$we' : '!$we' : '';
- my $time = $uzsu->{list}[$i]{event} eq "time" ? $uzsu->{list}[$i]{time} : '{'.$uzsu->{list}[$i]->{event} .'_abs("REAL",' . $uzsu->{list}[$i]->{timeOffset} * 60 . ',' . ($uzsu->{list}[$i]->{timeMin} ne '' ? '"' . $uzsu->{list}[$i]->{timeMin} . '"' : '') . ',' . ($uzsu->{list}[$i]->{timeMax} ne '' ? '"' . $uzsu->{list}[$i]->{timeMax} . '"' : '') . ')}';
- my $condition = UZSU_getCommand($uzsu->{list}[$i]{condition});
+ for (my $i = 0; $i < @{$uzsu->{list}}; $i++) {
+ if ($uzsu->{list}[$i]->{active}) {
+ my %rrule = UZSU_getRrules($uzsu->{list}[$i]{rrule});
+ my $holiday = $uzsu->{list}[$i]{holiday}{weekend} && $uzsu->{list}[$i]{holiday}{workday} ? '' : $uzsu->{list}[$i]{holiday}{weekend} ? $rrule{'BYDAY'} ne '' ? ',$we' : '$we' : $uzsu->{list}[$i]{holiday}{workday} ? $rrule{'BYDAY'} ne '' ? ',!$we' : '!$we' : '';
+ my $time = $uzsu->{list}[$i]{event} eq "time" ? $uzsu->{list}[$i]{time} : '{'.$uzsu->{list}[$i]->{event} . '_abs("REAL"' . ($uzsu->{list}[$i]->{timeOffset} ne '' ? ',' . $uzsu->{list}[$i]->{timeOffset} * 60 : '') . ($uzsu->{list}[$i]->{timeMin} ne '' ? ', "' . $uzsu->{list}[$i]->{timeMin} . '"' : '') . ($uzsu->{list}[$i]->{timeMax} ne '' ? ', "' . $uzsu->{list}[$i]->{timeMax} . '"' : '') . ')}';
+ my $condition = UZSU_getCommand($uzsu->{list}[$i]{condition});
- my $weekdayTimer = $rrule{'BYDAY'} . $holiday . ($rrule{'BYDAY'} ne '' || $holiday ne '' ? "|" : '') . $time . "|" . $uzsu->{list}[$i]{value};
- my $delayedExec = UZSU_getCommand($uzsu->{list}[$i]{delayedExec});
-
- fhem('defmod wdt_uzsu_' . $device . '_' . $i . ' WeekdayTimer ' . $device . ' en ' . $weekdayTimer . $condition);
- fhem('attr wdt_uzsu_' . $device . '_' . $i . ' room UZSU');
- fhem('attr wdt_uzsu_' . $device . '_' . $i . ' group ' . $device);
- fhem('setreading wdt_uzsu_' . $device . '_' . $i . ' weekdays ' . $weekdayTimer);
- fhem('defmod rg_uzsu_' . $device . ' readingsgroup wdt_uzsu_' . $device . '.*');
- fhem('attr rg_uzsu_' . $device . ' room UZSU');
- if ($delayedExec) {
- fhem('attr wdt_uzsu_' . $device . '_' . $i . ' delayedExecutionCond ' . $delayedExec);
- }
- }
- }
- if ($uzsu->{active}) {
- fhem('attr NAME=wdt_uzsu_' . $device . '_.*' . ' disable 0');
- }
- else {
- fhem('attr NAME=wdt_uzsu_' . $device . '_.*' . ' disable 1');
- }
- fhem('save', 1) if ($save eq 'save');
+ my $weekdayTimer = $rrule{'BYDAY'} . $holiday . ($rrule{'BYDAY'} ne '' || $holiday ne '' ? "|" : '') . $time . "|" . $uzsu->{list}[$i]{value};
+ my $delayedExec = UZSU_getCommand($uzsu->{list}[$i]{delayedExec});
+
+ fhem('defmod wdt_uzsu_' . $device . '_' . $i . ' WeekdayTimer ' . $device . ' en ' . $weekdayTimer . $condition);
+ fhem('attr wdt_uzsu_' . $device . '_' . $i . ' room UZSU');
+ fhem('attr wdt_uzsu_' . $device . '_' . $i . ' group ' . $device);
+ fhem('setreading wdt_uzsu_' . $device . '_' . $i . ' weekdays ' . $weekdayTimer);
+ fhem('defmod rg_uzsu_' . $device . ' readingsGroup wdt_uzsu_' . $device . '.*');
+ fhem('attr rg_uzsu_' . $device . ' room UZSU');
+ if ($delayedExec) {
+ fhem('attr wdt_uzsu_' . $device . '_' . $i . ' delayedExecutionCond ' . $delayedExec);
+ }
+ }
+ }
+ if ($uzsu->{active}) {
+ fhem('attr NAME=wdt_uzsu_' . $device . '_.*' . ' disable 0');
+ }
+ else {
+ fhem('attr NAME=wdt_uzsu_' . $device . '_.*' . ' disable 1');
+ }
+ fhem('save', 1) if ($save eq 'save');
}
###############################################################################
@@ -214,42 +225,49 @@ sub UZSU_getRrules($)
foreach (@a){
my ($key,$val) = split(/=/, $_);
$hash{$key} = $val;
- }
- return %hash;
-}
-
-sub UZSU_getCommand($)
-{
- my ($command) = @_;
- if($command->{active} && $command->{type} ne "String")
- {
- if($command->{deviceString} =~ /^AttrVal|InternalVal|ReadingsVal\("\S+"\s?,\s?"\S+"\s?,\s?"\S*"\)$/)
- {
- return ' (' . $command->{deviceString} . ' ' . $command->{type} . ' "' . $command->{value} . '")';
- }
- elsif($command->{deviceString} =~ /^Value\("\S+"\)$/)
- {
- return ' (' . $command->{deviceString} . ' ' . $command->{type} . ' "' . $command->{value} . '")';
- }
}
- elsif($command->{active} && $command->{type} eq "String" && $command->{deviceString} ne '')
+
+ if (exists($hash{'BYDAY'}))
{
- if($command->{deviceString} =~ /^fhem ".+"( if\(.+\))?$/)
- {
- return ' {' . $command->{deviceString} . '}';
- }
- else
- {
- return ' (' . $command->{deviceString} . ')';
- }
+ return %hash;
}
- else
- {
- return '';
+ else {
+ $hash{'BYDAY'} = '';
+ return %hash;
}
}
+sub UZSU_getCommand($)
+{
+ my ($command) = @_;
+
+ if($command->{active} && $command->{type} ne "String")
+ {
+ if($command->{deviceString} =~ /^AttrVal|InternalVal|ReadingsVal\("\S+"\s?,\s?"\S+"\s?,\s?"\S*"\)$/)
+ {
+ return ' (' . $command->{deviceString} . ' ' . $command->{type} . ' "' . $command->{value} . '")';
+ }
+ elsif($command->{deviceString} =~ /^Value\("\S+"\)$/)
+ {
+ return ' (' . $command->{deviceString} . ' ' . $command->{type} . ' "' . $command->{value} . '")';
+ }
+ }
+ elsif($command->{active} && $command->{type} eq "String" && $command->{deviceString} ne '')
+ {
+ if($command->{deviceString} =~ /^fhem ".+"( if\(.+\))?$/)
+ {
+ return ' {' . $command->{deviceString} . '}';
+ }
+ else
+ {
+ return ' (' . $command->{deviceString} . ')';
+ }
+ }
+ return '';
+}
+
+
###############################################################################
#
# Sammeln der Log-Daten in einem Dummy-Device
@@ -409,7 +427,7 @@ use warnings;
###############################################################################
# For use with UZSU-Widget in SV and UZSU-notify in fhem
# Setreading a device reading using JSON conversion (gadval => reading=decode_json() => setval => encode_json(reading) )
-# the reading ("uzsu") must be created manually for each UZSU-enabled device in fhem using "setreading uzsu {"active:false,"list":[]}
+# the reading ("uzsu") must be created manually for each UZSU-enabled device in fhem using "setreading uzsu {"active":false,"list":[]}
# in the fhem commandline
###############################################################################
@@ -434,8 +452,13 @@ sub UZSU(@)
if ($param->{cmd} eq 'send')
{
$param->{gad} = $gad;
- $param->{gadval} = main::fronthem_decodejson(main::ReadingsVal($device, $reading, '{}'));
- $param->{gads} = [];
+ # we could initialize the JSON with the default but for security reasons the user should do this willingly
+ # $param->{gadval} = main::fronthem_decodejson(main::ReadingsVal($device, $reading, '{"active": false, "list": []}'));
+ $param->{gadval} = main::fronthem_decodejson(main::ReadingsVal($device, $reading, '{}'));
+ $param->{gads} = [];
+ $param->{gadval}->{sunrise} = main::fronthem_sunrise("REAL");
+ $param->{gadval}->{sunset} = main::fronthem_sunset("REAL");
+
return undef;
}
elsif ($param->{cmd} eq 'rcv')
@@ -628,9 +651,9 @@ sub Plot(@)
my @args = @{$param->{args}};
my $cache = $param->{cache};
-
+
return "error $gad: converter syntax: missing paramter: name of database" if (@args != 1);
-
+
if ($param->{cmd} eq 'get') {
$param->{cmd} = 'send';
}
@@ -643,7 +666,7 @@ sub Plot(@)
"updatemode" => $updatemode,
"plotdata" => [],
};
-
+
if ($updatemode eq 'point') {
if ($mode eq "raw") {
push(@{$data[0]->{plotdata}[0]}, main::fronthem_ActualTimeStamp(main::gettimeofday()));
@@ -665,7 +688,7 @@ sub Plot(@)
else {
my $from = main::FmtDateTime(main::fronthem_Time(time(), $start));
$from =~ s/ /_/ig;
- my $to = main::FmtDateTime(main::fronthem_Time(time(), $end));
+ my $to = main::FmtDateTime(main::fronthem_Time(time(), $end));
$to =~ s/ /_/ig;
my $duration = "timerange";
@@ -811,11 +834,73 @@ sub Plotfile(@)
1;
=pod
+=item helper
+=item summary fronthem utility functions
+=item summary_DE fronthem Hilfsfunktionen
=begin html
-
-
fronthemUtils
-
-
+
+
+
+
+ fronthemUtils
+
+
+ This is a collection of converter functions that can be used with
+ fronthemDevice
+
+
+ Defined converter functions
+
+
AnAus invert state values an|aus to 0|1
+
Log send readings collected in dummy device as status.log
+
NumInvert direct invert of numerical values
+
Plot Plot data from fhem database
+ parameter for converter: Plot <name of database>
+ For MySQL databases the averaging mode allows time-weighted averaging.
+ The averaging time is specified by the tmin parameter in plot.period.
+ For details see here and other contribution in that thread
+
+
Plotfile Plot data from fhem filelog
+ parameter for converter: Plotfile <column> <regex>
+
+
UZSU for the control objects of the UZSU-Widget in smartVISU
+ attr global autosave must be set to 1 )
+
+
+
+
=end html
+
+=begin html_DE
+
+
+
+
+
+ fronthemUtils
+
+
+ Sammlung an Converter-Functionen, die mit
+ fronthemDevice eingesetzt werden können
+
+
+ Converter-Functionen
+
+
AnAus wandelt die Werte an|aus in 0|1 um und umgekehrt
+
Log Sendet Readings, die in einem Dummy-Device gesammelt werden als status.log
+
NumInvert wandelt numerische Werte direkt um
+
Plot Plot-Daten aus der FHEM-Datenbank
+ Parameter für converter: Plot <name of database>
+
+
Plotfile Plot-Daten aus filelog von FHEM
+ Parameter für converter: Plotfile <column> <regex>
+
+
UZSU für Schaltzeiten-Objekte des UZSU-Widgets in smartVISU
+ attr global autosave muss dafür auf den Wert 1 gesetzt werden)
+
+
+
+
+=end html_DE
=cut
diff --git a/controls_fronthem.txt b/controls_fronthem.txt
index 75d3e6a..ea7bfee 100644
--- a/controls_fronthem.txt
+++ b/controls_fronthem.txt
@@ -1,6 +1,6 @@
UPD 2022-09-08_18:54:48 27037 FHEM/01_fronthem.pm
UPD 2022-09-08_19:16:02 28957 FHEM/31_fronthemDevice.pm
-UPD 2026-02-17_21:30:22 22559 FHEM/99_fronthemUtils.pm
+UPD 2026-02-17_21:30:22 26641 FHEM/99_fronthemUtils.pm
UPD 2015-02-13_21:23:35 7435 FHEM/fhwebsocket.pm
UPD 2019-01-03_11:22:09 9811 FHEM/fhconverter.pm
UPD 2022-09-08_18:55:13 14158 www/pgm2/fronthemEditor.js
From a3757f4575ea4a11be376c38fd128cf1a52b473d Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Thu, 19 Feb 2026 15:17:05 +0100
Subject: [PATCH 15/19] make websocket port and message size configurable
---
CHANGED | 4 +
FHEM/01_fronthem.pm | 363 ++++++++++++++++++++++++------------------
FHEM/fhwebsocket.pm | 2 +-
controls_fronthem.txt | 8 +-
4 files changed, 216 insertions(+), 161 deletions(-)
diff --git a/CHANGED b/CHANGED
index 1740f1c..609d3ee 100644
--- a/CHANGED
+++ b/CHANGED
@@ -1,3 +1,7 @@
+2026-02-19
+- transmit sunrise/sunset for UZSU
+- make websocket port and message size configurable
+
2023-12-18
- extended plot durations to allow usage of all smartVISU plot features
diff --git a/FHEM/01_fronthem.pm b/FHEM/01_fronthem.pm
index 2e31e4b..2af5415 100644
--- a/FHEM/01_fronthem.pm
+++ b/FHEM/01_fronthem.pm
@@ -7,11 +7,11 @@
# fixed:
# issue with some JSON module at startup
-# perl before 5.14 issue
+# perl before 5.14 issue
# remove debug output
# open:
-# UTF8 conversation
+# UTF8 conversation
# num converter with negative values
# 99er converter (see reload)
@@ -30,6 +30,7 @@ use IO::Select;
use fhwebsocket;
use JSON;
use utf8;
+use Encode;
use Data::Dumper;
@@ -40,14 +41,15 @@ fronthem_Initialize(@)
{
my ($hash) = @_;
-
+
$hash->{DefFn} = "fronthem_Define";
$hash->{SetFn} = "fronthem_Set";
$hash->{ReadFn} = "fronthem_Read";
+ $hash->{AttrFn} = "fronthem_Attr";
$hash->{UndefFn} = "fronthem_Undef";
$hash->{NotifyFn} = "fronthem_Notify";
$hash->{ShutdownFn} = "fronthem_Shutdown";
- $hash->{AttrList} = "configFile ".$readingFnAttributes;
+ $hash->{AttrList} = "configFile maxSendSize port ".$readingFnAttributes;
}
sub
@@ -56,47 +58,28 @@ fronthem_Define($$)
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
my $name = $a[0];
- my $cfg;
$hash->{helper}->{COMMANDSET} = 'save';
- #TODO move it to "initialized"
- fronthem_ReadCfg($hash, 'fronthem.cfg');
-
- my $port = 16384;
+ $hash->{helper}->{ipcPort} = 16384;
# create and register server ipc parent (listener == socket)
do
{
$hash->{helper}->{listener} = IO::Socket::INET->new(
LocalHost => 'localhost',
- LocalPort => $port,
- Listen => SOMAXCONN,
+ LocalPort => $hash->{helper}->{ipcPort},
+ Listen => SOMAXCONN,
Reuse => 1 );
- $port++;
+ $hash->{helper}->{ipcPort} += 1;
} until (defined($hash->{helper}->{listener}));
- $port -= 1;
+ $hash->{helper}->{ipcPort} -= 1;
my $flags = fcntl($hash->{helper}->{listener}, F_GETFL, 0) or return "error shaping ipc: $!";
fcntl($hash->{helper}->{listener}, F_SETFL, $flags | O_NONBLOCK) or return "error shaping ipc: $!";
- Log3 ($hash, 2, "$hash->{NAME}: ipc listener opened at port $port");
+ Log3 ($hash, 2, "$hash->{NAME}: ipc listener opened at port $hash->{helper}->{ipcPort}");
$hash->{TCPDev} = $hash->{helper}->{listener};
$hash->{FD} = $hash->{helper}->{listener}->fileno();
$selectlist{"$name:ipcListener"} = $hash;
- $hash->{helper}->{main}->{state} = 'run';
- if ($init_done)
- {
- # TODO set initial readings
- }
-
- # prepare forking the ws server
- $cfg->{hash} = $hash;
- $cfg->{id} = 'ws';
- $cfg->{port} = 2121;
- $cfg->{ipcPort} = $port;
- # preserve
- $hash->{helper}->{main}->{state} = 'run';
- fronthem_StartWebsocketServer($cfg);
-
return undef;
}
@@ -114,8 +97,8 @@ fronthem_Set(@)
}
#ipc, accept from forked socket server
-sub
-fronthem_Read(@)
+sub
+fronthem_Read(@)
{
my ($hash) = @_;
my $ipcClient = $hash->{helper}->{listener}->accept();
@@ -123,7 +106,7 @@ fronthem_Read(@)
fcntl($ipcClient, F_SETFL, $flags | O_NONBLOCK) or return "error shaping ipc client: $!";
# TODO connections from other then localhost possible||usefull ? evaluate the need ...
-
+
my $ipcHash;
$ipcHash->{TCPDev} = $ipcClient;
$ipcHash->{FD} = $ipcClient->fileno();
@@ -141,7 +124,7 @@ fronthem_Read(@)
return undef;
}
-sub
+sub
fronthem_Notify($$)
{
my ($hash, $ntfyDev) = @_;
@@ -158,7 +141,19 @@ fronthem_Notify($$)
#Log3 ($hash, 1, "in $e[0]"); #TODO remove
if ($e[0] eq 'INITIALIZED')
{
- #
+ fronthem_ReadCfg($hash, 'fronthem.cfg');
+ $hash->{MAXSENDSIZE} = AttrVal( $name, "maxSendSize", "65536" );
+ $hash->{PORT} = AttrVal( $name, "port", "2121" );
+ # prepare forking the ws server
+ my $cfg;
+ $cfg->{hash} = $hash;
+ $cfg->{id} = 'ws';
+ $cfg->{port} = $hash->{PORT};
+ $cfg->{ipcPort} = $hash->{helper}->{ipcPort};
+ $cfg->{max_send_size} = $hash->{MAXSENDSIZE};
+ # preserve
+ $hash->{helper}->{main}->{state} = 'run';
+ fronthem_StartWebsocketServer($cfg);
}
elsif ($e[0] eq 'RENAMED')
{
@@ -173,21 +168,45 @@ fronthem_Notify($$)
elsif ( $ntfyDevName ne $name ) {
return undef
}
-
+
foreach my $change (@{$ntfyDev->{CHANGED}}) {
- readingsBeginUpdate($hash);
- #Log3 $name, 1, "fronthem $name: change: $change.";
-
- if ($change =~ /^(.+): (.+)/) {
- if($1 eq "ws") {
- readingsBulkUpdate($hash, "state", $2);
- }
- }
+ readingsBeginUpdate($hash);
+ #Log3 $name, 1, "fronthem $name: change: $change.";
+
+ if ($change =~ /^(.+): (.+)/) {
+ if($1 eq "ws") {
+ readingsBulkUpdate($hash, "state", $2);
+ }
+ }
}
readingsEndUpdate($hash, 1);
return undef;
}
+sub
+fronthem_Attr(@)
+{
+ my ($type, $devName, $attrName, @param) = @_;
+ my $hash = $defs{$devName};
+ #Log3 $devName, 1, "01_fronthem: $type - $devName - $attrName - $param[0]";
+
+ if($type eq "set" && $attrName eq "maxSendSize" && $param[0]) {
+ $hash->{MAXSENDSIZE} = $param[0];
+ }
+
+ if($type eq "set" && $attrName eq "port" && $param[0]) {
+ $hash->{PORT} = $param[0];
+ }
+
+ if($type eq "del" && $attrName eq "maxSendSize") {
+ $hash->{MAXSENDSIZE} = 65536;
+ }
+
+ if($type eq "del" && $attrName eq "port") {
+ $hash->{PORT} = 2121;
+ }
+}
+
sub
fronthem_Undef(@)
{
@@ -208,8 +227,8 @@ fronthem_Shutdown(@)
}
#ipc, read msg from forked socket server
-sub
-fronthem_ipcRead($)
+sub
+fronthem_ipcRead($)
{
my ($ipcHash) = @_;
my $msg = "";
@@ -243,14 +262,15 @@ fronthem_ipcRead($)
if (defined($ipcHash->{registered}))
{
- $id = $ipcHash->{registered};
+ $id = $ipcHash->{registered};
# TODO check if a dispatcher is set
- eval
+ eval
{
+ $msg = Encode::encode('UTF-8', $msg);
$up = decode_json($msg);
-
+
Log3 ($ipcHash->{PARENT}, $up->{log}->{level}, "ipc $ipcHash->{NAME} ($id): $up->{log}->{text}") if (exists($up->{log}) && (($up->{log}->{cmd} || '') eq 'log'));
- #keep global cfg up to date, add new items
+ #keep global cfg up to date, add new items
if (exists($up->{message}) && (($up->{message}->{cmd} || '') eq 'monitor'))
{
foreach my $item (@{$up->{message}->{items}})
@@ -258,40 +278,41 @@ fronthem_ipcRead($)
$ipcHash->{PARENT}->{helper}->{config}->{$item}->{type} = 'item' unless defined($ipcHash->{PARENT}->{helper}->{config}->{$item}->{type});
}
}
- if (exists($up->{message}) && (($up->{message}->{cmd} || '') eq 'plot'))
+ if (exists($up->{message}) && (($up->{message}->{cmd} || '') eq 'plot'))
{
- foreach my $item (@{$up->{message}->{items}})
- {
- my $gad = $item->{item};
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{type} = 'plot';
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{mode} = $item->{mode};
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{start} = $item->{start};
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{end} = $item->{end};
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{count} = $item->{count};
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{interval} = $item->{interval};
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{updatemode} = $item->{updatemode};
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{datamode} = $item->{datamode};
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{gad} = $item->{item} if (defined($item->{item}));
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{gads} = $item->{items} if (defined($item->{items}));
- }
+ foreach my $item (@{$up->{message}->{items}})
+ {
+ my $gad = $item->{item};
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{type} = 'plot';
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{mode} = $item->{mode};
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{start} = $item->{start};
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{end} = $item->{end};
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{count} = $item->{count};
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{interval} = $item->{interval};
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{updatemode} = $item->{updatemode};
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{datamode} = $item->{datamode};
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{gad} = $item->{item} if (defined($item->{item}));
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{gads} = $item->{items} if (defined($item->{items}));
+ }
}
- if (exists($up->{message}) && (($up->{message}->{cmd} || '') eq 'log'))
- {
- foreach my $item (@{$up->{message}->{items}})
- {
- my $gad = $item->{item};
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{type} = 'log';
- $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{size} = $item->{size};
- }
- }
+ if (exists($up->{message}) && (($up->{message}->{cmd} || '') eq 'log'))
+ {
+ foreach my $item (@{$up->{message}->{items}})
+ {
+ my $gad = $item->{item};
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{type} = 'log';
+ $ipcHash->{PARENT}->{helper}->{config}->{$gad}->{size} = $item->{size};
+ }
+ }
fronthem_ProcessDeviceMsg($ipcHash, $up) if (exists($up->{message}));
+ } or do {
+ Log3 ($ipcHash->{PARENT}, 2, "ipc $ipcHash->{NAME} ($id): error $@ decoding ipc msg $msg") if ($@);
};
- Log3 ($ipcHash->{PARENT}, 2, "ipc $ipcHash->{NAME} ($id): error $@ decoding ipc msg $msg") if ($@);
}
else
{
# first incoming msg, must contain id:pid (name) of forked child
- # security check, see if we are waiting for. id and pid should be registered in $hash->{helper}->{ipc}->{$id}->{pid} before incoming will be accepted
+ # security check, see if we are waiting for. id and pid should be registered in $hash->{helper}->{ipc}->{$id}->{pid} before incoming will be accepted
if (($msg =~ m/^(\w+):(\d+)$/) && ($ipcHash->{PARENT}->{helper}->{ipc}->{$1}->{pid} eq $2))
{
($id,$pid) = ($1, $2);
@@ -316,7 +337,7 @@ fronthem_ipcRead($)
# id: eq ws,wss
# msg: whats to tell
-sub
+sub
fronthem_ipcWrite(@)
{
my ($hash, $id, $msg) = @_;
@@ -330,19 +351,19 @@ fronthem_ipcWrite(@)
my $out = to_json($msg)."\n";
my $lin = length $out;
my $result = $hash->{helper}->{ipc}->{$id}->{sock}->{TCPDev}->send($out);
-
- if (!defined($result))
+
+ if (!defined($result))
{
Log3 ($hash, 1, "$hash->{NAME} send to $id (ipc to child) unkown error");
fronthem_DisconnectClients($hash, $id);
return undef;
}
- if ($result != $lin)
+ if ($result != $lin)
{
Log3 ($hash, 1, "$hash->{NAME} send to $id (ipc to child) in $lin send $result");
return undef;
}
-
+
return undef;
}
@@ -360,7 +381,7 @@ fronthem_DisconnectClients(@)
return undef;
}
-# forced disonnect
+# forced disonnect
sub
fronthem_DisconnectClient(@)
{
@@ -404,10 +425,10 @@ fronthem_ReadCfg(@)
my $data;
my $filtered->{config} = {};
-
+
if (length($json_text))
{
- eval
+ eval
{
my $json = JSON->new->utf8;
$data = $json->decode($json_text);
@@ -427,21 +448,21 @@ fronthem_ReadCfg(@)
$filtered->{config}->{$key}->{reading} = $data->{config}->{$key}->{reading};
$filtered->{config}->{$key}->{converter} = $data->{config}->{$key}->{converter};
$filtered->{config}->{$key}->{set} = $data->{config}->{$key}->{set};
-
- if ($data->{config}->{$key}->{type} eq 'plot')
- {
- $filtered->{config}->{$key}->{start} = $data->{config}->{$key}->{start};
- $filtered->{config}->{$key}->{end} = $data->{config}->{$key}->{end};
- $filtered->{config}->{$key}->{mode} = $data->{config}->{$key}->{mode};
- $filtered->{config}->{$key}->{interval} = $data->{config}->{$key}->{interval};
- $filtered->{config}->{$key}->{updatemode} = $data->{config}->{$key}->{updatemode};
- $filtered->{config}->{$key}->{datamode} = $data->{config}->{$key}->{datamode};
- }
-
- if ($data->{config}->{$key}->{type} eq 'log')
- {
- $filtered->{config}->{$key}->{size} = $data->{config}->{$key}->{size};
- }
+
+ if ($data->{config}->{$key}->{type} eq 'plot')
+ {
+ $filtered->{config}->{$key}->{start} = $data->{config}->{$key}->{start};
+ $filtered->{config}->{$key}->{end} = $data->{config}->{$key}->{end};
+ $filtered->{config}->{$key}->{mode} = $data->{config}->{$key}->{mode};
+ $filtered->{config}->{$key}->{interval} = $data->{config}->{$key}->{interval};
+ $filtered->{config}->{$key}->{updatemode} = $data->{config}->{$key}->{updatemode};
+ $filtered->{config}->{$key}->{datamode} = $data->{config}->{$key}->{datamode};
+ }
+
+ if ($data->{config}->{$key}->{type} eq 'log')
+ {
+ $filtered->{config}->{$key}->{size} = $data->{config}->{$key}->{size};
+ }
}
}
@@ -459,7 +480,7 @@ fronthem_WriteCfg(@)
$cfgContent->{version} = '1.0';
$cfgContent->{modul} = 'fronthem-server';
-
+
foreach my $key (keys %{ $hash->{helper}->{config} })
{
if ($hash->{helper}->{config}->{$key}->{type} eq 'item')
@@ -470,29 +491,29 @@ fronthem_WriteCfg(@)
$cfgContent->{config}->{$key}->{converter} = $hash->{helper}->{config}->{$key}->{converter};
$cfgContent->{config}->{$key}->{set} = $hash->{helper}->{config}->{$key}->{set};
}
- elsif ($hash->{helper}->{config}->{$key}->{type} eq 'log')
+ elsif ($hash->{helper}->{config}->{$key}->{type} eq 'log')
{
$cfgContent->{config}->{$key}->{type} = $hash->{helper}->{config}->{$key}->{type};
- $cfgContent->{config}->{$key}->{size} = $hash->{helper}->{config}->{$key}->{size};
+ $cfgContent->{config}->{$key}->{size} = $hash->{helper}->{config}->{$key}->{size};
$cfgContent->{config}->{$key}->{device} = $hash->{helper}->{config}->{$key}->{device};
$cfgContent->{config}->{$key}->{reading} = $hash->{helper}->{config}->{$key}->{reading};
$cfgContent->{config}->{$key}->{converter} = $hash->{helper}->{config}->{$key}->{converter};
$cfgContent->{config}->{$key}->{set} = $hash->{helper}->{config}->{$key}->{set};
}
- elsif ($hash->{helper}->{config}->{$key}->{type} eq 'plot')
+ elsif ($hash->{helper}->{config}->{$key}->{type} eq 'plot')
{
$cfgContent->{config}->{$key}->{type} = $hash->{helper}->{config}->{$key}->{type};
$cfgContent->{config}->{$key}->{device} = $hash->{helper}->{config}->{$key}->{device};
$cfgContent->{config}->{$key}->{reading} = $hash->{helper}->{config}->{$key}->{reading};
$cfgContent->{config}->{$key}->{converter} = $hash->{helper}->{config}->{$key}->{converter};
$cfgContent->{config}->{$key}->{set} = $hash->{helper}->{config}->{$key}->{set};
-
- $cfgContent->{config}->{$key}->{start} = $hash->{helper}->{config}->{$key}->{start};
- $cfgContent->{config}->{$key}->{end} = $hash->{helper}->{config}->{$key}->{end};
- $cfgContent->{config}->{$key}->{mode} = $hash->{helper}->{config}->{$key}->{mode};
- $cfgContent->{config}->{$key}->{interval} = $hash->{helper}->{config}->{$key}->{interval};
- $cfgContent->{config}->{$key}->{updatemode} = $hash->{helper}->{config}->{$key}->{updatemode};
- $cfgContent->{config}->{$key}->{datamode} = $hash->{helper}->{config}->{$key}->{datamode};
+
+ $cfgContent->{config}->{$key}->{start} = $hash->{helper}->{config}->{$key}->{start};
+ $cfgContent->{config}->{$key}->{end} = $hash->{helper}->{config}->{$key}->{end};
+ $cfgContent->{config}->{$key}->{mode} = $hash->{helper}->{config}->{$key}->{mode};
+ $cfgContent->{config}->{$key}->{interval} = $hash->{helper}->{config}->{$key}->{interval};
+ $cfgContent->{config}->{$key}->{updatemode} = $hash->{helper}->{config}->{$key}->{updatemode};
+ $cfgContent->{config}->{$key}->{datamode} = $hash->{helper}->{config}->{$key}->{datamode};
}
}
@@ -523,8 +544,8 @@ fronthem_CreateListen(@)
{
my $gad = $hash->{helper}->{config}->{$key};
$listen->{$gad->{device}}->{$gad->{reading}}->{$key} = $hash->{helper}->{config}->{$key} if ((defined($gad->{device})) && (defined($gad->{reading})));
- $plotlisten->{$gad->{device}}->{$gad->{reading}}->{$key} = $hash->{helper}->{config}->{$key} if ((defined($gad->{device})) && (defined($gad->{reading})) && $gad->{type} eq 'plot');
- $loglisten->{$gad->{device}}->{$gad->{reading}}->{$key} = $hash->{helper}->{config}->{$key} if ((defined($gad->{device})) && (defined($gad->{reading})) && $gad->{type} eq 'log');
+ $plotlisten->{$gad->{device}}->{$gad->{reading}}->{$key} = $hash->{helper}->{config}->{$key} if ((defined($gad->{device})) && (defined($gad->{reading})) && $gad->{type} eq 'plot');
+ $loglisten->{$gad->{device}}->{$gad->{reading}}->{$key} = $hash->{helper}->{config}->{$key} if ((defined($gad->{device})) && (defined($gad->{reading})) && $gad->{type} eq 'log');
}
$hash->{helper}->{listen} = $listen;
$hash->{helper}->{plot} = $plotlisten;
@@ -544,14 +565,14 @@ fronthem_ProcessDeviceMsg(@)
{
my ($ipcHash, $msg) = @_;
- my $hash = $ipcHash->{PARENT};
+ my $hash = $ipcHash->{PARENT};
my $connection = $ipcHash->{registered}.':'.$msg->{'connection'};
my $sender = $msg->{'sender'};
my $identity = $msg->{'identity'};
my $message = $msg->{'message'};
-
- #TODO:
+
+ #TODO:
# check if device with given identity is already connected
# if so, reject the connection and give it a hint why
@@ -566,7 +587,7 @@ fronthem_ProcessDeviceMsg(@)
}
else
{
- #TODO error logging, disconnect
+ #TODO error logging, disconnect
}
}
elsif((($message->{cmd} || '') eq 'handshake') && ($hash->{helper}->{receiver}->{$connection}->{state} eq 'connecting') )
@@ -579,7 +600,7 @@ fronthem_ProcessDeviceMsg(@)
{
$hash->{helper}->{receiver}->{$connection}->{device} = $key;
$hash->{helper}->{receiver}->{$connection}->{state} = 'connected';
- #build sender
+ #build sender
$hash->{helper}->{sender}->{$key}->{connection} = $ipcHash->{registered};
$hash->{helper}->{sender}->{$key}->{ressource} = $msg->{'connection'};
$hash->{helper}->{sender}->{$key}->{state} = 'connected';
@@ -609,7 +630,7 @@ fronthem_ProcessDeviceMsg(@)
{
$hash->{helper}->{receiver}->{$connection}->{device} = $key;
$hash->{helper}->{receiver}->{$connection}->{state} = 'connected';
- #build sender
+ #build sender
$hash->{helper}->{sender}->{$key}->{connection} = $ipcHash->{registered};
$hash->{helper}->{sender}->{$key}->{ressource} = $msg->{'connection'};
$hash->{helper}->{sender}->{$key}->{state} = 'connected';
@@ -617,8 +638,8 @@ fronthem_ProcessDeviceMsg(@)
}
}
}
-
- if(($message->{cmd} || '') eq 'disconnect')
+
+ if(($message->{cmd} || '') eq 'disconnect')
{
my $key = $hash->{helper}->{receiver}->{$connection}->{device};
@@ -627,8 +648,8 @@ fronthem_ProcessDeviceMsg(@)
{
my $devHash = $defs{$key};
fronthemDevice_fromDriver($devHash, $msg);
- delete($hash->{helper}->{sender}->{$key});
- }
+ delete($hash->{helper}->{sender}->{$key});
+ }
return undef;
}
@@ -645,7 +666,7 @@ fronthem_ProcessDeviceMsg(@)
#msg is hash from fhem fronthemDevice instance, will be dispatched to forked client, and further to sv client
#msg->receiver = speaking name (eg tab)
#msg->ressource
-#msg->message->cmd
+#msg->message->cmd
sub
fronthem_FromDevice(@)
{
@@ -661,7 +682,7 @@ fronthem_FromDevice(@)
#ressource within ipc child, leave blank if you want t talk with the process itself
$msg->{ressource} = $hash->{helper}->{sender}->{$device}->{ressource};
fronthem_ipcWrite($hash, $connection, $msg);
- return undef;
+ return undef;
}
###############################################################################
@@ -671,8 +692,9 @@ fronthem_FromDevice(@)
sub
fronthem_StartWebsocketServer(@)
{
- my ($cfg) = @_;
+ my ($cfg) = @_;
my $id = $cfg->{id};
+ my $name = $cfg->{hash}->{NAME};
my $pid = fork();
return "Error while try to fork $id: $!" unless (defined $pid);
@@ -688,10 +710,10 @@ fronthem_StartWebsocketServer(@)
setsid();
# close open handles
- close STDOUT;
+ close STDOUT;
open STDOUT, '>/dev/null';
close STDIN;
- close STDERR;
+ close STDERR;
# open STDERR, '>/dev/null';
open STDERR, '>>', "fronthem.err";
@@ -718,6 +740,7 @@ fronthem_StartWebsocketServer(@)
$ws->{'ipc'} = $ipc;
$ws->{id} = $id;
$ws->{buffer} = '';
+ $ws->{max_send_size} = $cfg->{max_send_size};
$ws->watch_readable($ipc->fileno() => \&fronthem_wsIpcRead);
fronthem_forkLog3 ($ws->{ipc}, 1, "$ws->{id} could not open port $cfg->{port}") unless $ws->start;
@@ -805,7 +828,7 @@ fronthem_wsIpcRead(@)
my ($serv, $fh) = @_;
my $msg = '';
my $rv;
-
+
$rv = $serv->{'ipc'}->recv($msg, POSIX::BUFSIZ, 0);
unless (defined($rv) && length $msg) {
$serv -> shutdown();
@@ -815,9 +838,12 @@ fronthem_wsIpcRead(@)
while (($serv->{buffer} =~ m/\n/) && (($msg, $serv->{buffer}) = split /\n/, $serv->{buffer}, 2))
{
eval {
+ $msg = Encode::encode('UTF-8', $msg);
$msg = decode_json($msg);
+ } or do {
+ fronthem_forkLog3 ($serv->{ipc}, 1, "$serv->{id} ipc decoding error $@") if ($@);
+ $msg = {};
};
- fronthem_forkLog3 ($serv->{ipc}, 1, "$serv->{id} ipc decoding error $@") if ($@);
fronthem_wsProcessInboundCmd($serv, $msg);
}
return undef;
@@ -825,7 +851,7 @@ fronthem_wsIpcRead(@)
#msg->receiver = speaking name (eg tab)
#msg->ressource
-#msg->message->cmd
+#msg->message->cmd
sub
fronthem_wsProcessInboundCmd(@)
@@ -847,37 +873,62 @@ fronthem_wsProcessInboundCmd(@)
=item summary_DE fronthem websocket Schnittstelle für FHEM
=begin html
-
-
-
-
- fronthem
-
-
- Define
-
- define <name> fronthem
-
-
-
+
+
+
+
+ fronthem
+
+
+ Define
+
+ define <name> fronthem
+
+
+
+ Attributes
+
+ Attributes (restart of FHEM necessary in case of changes):
+
Log send readings collected in dummy device as status.log
-
NumInvert direct invert of numerical values
-
Plot Plot data from fhem database
- parameter for converter: Plot <name of database>
- For MySQL databases the averaging mode allows time-weighted averaging.
- The averaging time is specified by the tmin parameter in plot.period.
- For details see here and other contribution in that thread
-
-
Plotfile Plot data from fhem filelog
- parameter for converter: Plotfile <column> <regex>
-
-
UZSU for the control objects of the UZSU-Widget in smartVISU
+
+
AnAus invert state values an|aus to 0|1
+
JSON send / receive readings as JSON objects
+
Log send readings collected in dummy device as status.log
+
NumInvert direct invert of numerical values
+
Plot Plot data from fhem database
+ parameter for converter: Plot <name of database>
+ For MySQL databases the averaging mode allows time-weighted averaging.
+ The averaging time is specified by the tmin parameter in plot.period.
+ For details see here and other contribution in that thread
+
+
Plotfile Plot data from fhem filelog
+ parameter for converter: Plotfile <column> <regex>
+
+
UZSU for the control objects of the UZSU-Widget in smartVISU attr global autosave must be set to 1 )
-
-
+
+
-
+
=end html
=begin html_DE
@@ -886,21 +935,22 @@ sub Plotfile(@)
Converter-Functionen
-
-
AnAus wandelt die Werte an|aus in 0|1 um und umgekehrt
-
Log Sendet Readings, die in einem Dummy-Device gesammelt werden als status.log
-
NumInvert wandelt numerische Werte direkt um
-
Plot Plot-Daten aus der FHEM-Datenbank
- Parameter für converter: Plot <name of database>
-
-
Plotfile Plot-Daten aus filelog von FHEM
- Parameter für converter: Plotfile <column> <regex>
-
-
UZSU für Schaltzeiten-Objekte des UZSU-Widgets in smartVISU
- attr global autosave muss dafür auf den Wert 1 gesetzt werden)
-
-
+
+
AnAus wandelt die Werte an|aus in 0|1 um und umgekehrt
+
JSON sendet / empfängt Readings als JSON-Objekte
+
Log Sendet Readings, die in einem Dummy-Device gesammelt werden als status.log
+
NumInvert wandelt numerische Werte direkt um
+
Plot Plot-Daten aus der FHEM-Datenbank
+ Parameter für converter: Plot <name of database>
+
+
Plotfile Plot-Daten aus filelog von FHEM
+ Parameter für converter: Plotfile <column> <regex>
+
+
UZSU für Schaltzeiten-Objekte des UZSU-Widgets in smartVISU
+ attr global autosave muss dafür auf den Wert 1 gesetzt werden)
+
+
-
+
=end html_DE
=cut
diff --git a/controls_fronthem.txt b/controls_fronthem.txt
index 5ddd4d5..2e76c76 100644
--- a/controls_fronthem.txt
+++ b/controls_fronthem.txt
@@ -1,6 +1,6 @@
UPD 2026-02-19_14:51:18 30263 FHEM/01_fronthem.pm
UPD 2022-11-19_18:56:36 29911 FHEM/31_fronthemDevice.pm
-UPD 2026-02-19_13:26:53 26641 FHEM/99_fronthemUtils.pm
+UPD 2026-02-19_15:22:54 30391 FHEM/99_fronthemUtils.pm
UPD 2026-02-18_13:38:59 7727 FHEM/fhwebsocket.pm
UPD 2019-01-03_11:22:09 9811 FHEM/fhconverter.pm
UPD 2022-09-08_18:55:13 14158 www/pgm2/fronthemEditor.js
From 801a35d6bf3d1e33222b4bdedcd02b7f3f2468b7 Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Sat, 21 Feb 2026 09:10:30 +0100
Subject: [PATCH 17/19] Update README.md
---
README.md | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 020e8b7..af277ca 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,11 @@
# fronthem
-fronthem websocket Schnittstelle für FHEM
+fronthem websocket Schnittstelle für FHEM zur Verbindung mit smartVISU
+
+# Wiki
+https://wiki.fhem.de/wiki/Kategorie:Fronthem/smartVISU
+
+# Support Forum
+https://forum.fhem.de/index.php/board,72.0.html
+
+# Update in FHEM
+update force https://raw.githubusercontent.com/wvhn/fronthem/master/controls_fronthem.txt
From 5efb561f14bc780076b7baad94a342f3b17dbebb Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Mon, 23 Feb 2026 11:59:16 +0100
Subject: [PATCH 18/19] rename JSON converter to JSONdata
---
CHANGED | 2 +-
FHEM/99_fronthemUtils.pm | 2 +-
controls_fronthem.txt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/CHANGED b/CHANGED
index 6c23f24..692f335 100644
--- a/CHANGED
+++ b/CHANGED
@@ -1,7 +1,7 @@
2026-02-19
- transmit sunrise/sunset for UZSU
- make websocket port and message size configurable
-- new JSON converter
+- new JSONdata converter
2023-12-18
- extended plot durations to allow usage of all smartVISU plot features
diff --git a/FHEM/99_fronthemUtils.pm b/FHEM/99_fronthemUtils.pm
index 3cbe985..febc75a 100644
--- a/FHEM/99_fronthemUtils.pm
+++ b/FHEM/99_fronthemUtils.pm
@@ -482,7 +482,7 @@ sub UZSU(@)
#
###############################################################################
-sub JSON(@)
+sub JSONdata(@)
{
my ($param) = @_;
my $cmd = $param->{cmd};
diff --git a/controls_fronthem.txt b/controls_fronthem.txt
index 2e76c76..ff05798 100644
--- a/controls_fronthem.txt
+++ b/controls_fronthem.txt
@@ -1,6 +1,6 @@
UPD 2026-02-19_14:51:18 30263 FHEM/01_fronthem.pm
UPD 2022-11-19_18:56:36 29911 FHEM/31_fronthemDevice.pm
-UPD 2026-02-19_15:22:54 30391 FHEM/99_fronthemUtils.pm
+UPD 2026-02-23_11:42:45 30395 FHEM/99_fronthemUtils.pm
UPD 2026-02-18_13:38:59 7727 FHEM/fhwebsocket.pm
UPD 2019-01-03_11:22:09 9811 FHEM/fhconverter.pm
UPD 2022-09-08_18:55:13 14158 www/pgm2/fronthemEditor.js
From ebc9f4edb83e08a73c6b5f571e36c01f4db1eb59 Mon Sep 17 00:00:00 2001
From: wvhn <17801971+wvhn@users.noreply.github.com>
Date: Tue, 24 Feb 2026 15:13:33 +0100
Subject: [PATCH 19/19] fix EOL and adjust file sizes
LF instead of CRLF - update ignores CR and so the file sizes don't match
---
controls_fronthem.txt | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/controls_fronthem.txt b/controls_fronthem.txt
index ff05798..00cca95 100644
--- a/controls_fronthem.txt
+++ b/controls_fronthem.txt
@@ -1,7 +1,7 @@
-UPD 2026-02-19_14:51:18 30263 FHEM/01_fronthem.pm
-UPD 2022-11-19_18:56:36 29911 FHEM/31_fronthemDevice.pm
-UPD 2026-02-23_11:42:45 30395 FHEM/99_fronthemUtils.pm
-UPD 2026-02-18_13:38:59 7727 FHEM/fhwebsocket.pm
+UPD 2026-02-24_15:02:24 29329 FHEM/01_fronthem.pm
+UPD 2022-11-24_15:04:07 28957 FHEM/31_fronthemDevice.pm
+UPD 2026-02-24_15:04:18 29439 FHEM/99_fronthemUtils.pm
+UPD 2026-02-24_15:04:48 7476 FHEM/fhwebsocket.pm
UPD 2019-01-03_11:22:09 9811 FHEM/fhconverter.pm
UPD 2022-09-08_18:55:13 14158 www/pgm2/fronthemEditor.js
UPD 2015-01-16_11:51:34 462 www/images/default/arrow-down.svg