diff --git a/fhem/FHEM/lib/AttrTemplate/mqtt2.template b/fhem/FHEM/lib/AttrTemplate/mqtt2.template index 2184f78bc..8216e8338 100644 --- a/fhem/FHEM/lib/AttrTemplate/mqtt2.template +++ b/fhem/FHEM/lib/AttrTemplate/mqtt2.template @@ -1172,14 +1172,48 @@ attr DEVICE setList \ attr DEVICE periodicCmd temperature:55 attr DEVICE devStateIcon LOCKED:secur_lock:btnLock+UNLOCK UNLOCKED:secur_open:btnLock+LOCK attr DEVICE webCmd desired-temp -attr DEVICE jsonMap current_heating_setpoint:desired-temp local_temperature:temperature Battery:batteryPercent system_mode:mode voltage:batterymV +attr DEVICE jsonMap current_heating_setpoint:desired-temp local_temperature:temperature Battery:batteryPercent system_mode:mode voltage:batterymV child_lock:btnLock attr DEVICE stateFormat btnLock\ Measured: temperature Battery: batteryPercent % attr DEVICE userReadings batteryState:battery_low.* {ReadingsVal($name,'battery_low','false') eq 'false'?'ok':'low'}, batteryVoltage:batterymV.* {ReadingsNum($name,'batterymV',0)/1000} attr DEVICE model zigbee2mqtt_thermostat_without_weekrofile set DEVICE attrTemplate speechcontrol_type_thermostat deletereading -q DEVICE (?!associatedWith|IODev).* -setreading DEVICE attrTemplateVersion 20240108 +setreading DEVICE attrTemplateVersion 20240402 + + +#contributed by clumsy via pm +name:zigbee2mqtt_thermostat_with_weekrofile_5_1_1 +desc: Developed for Models Moes BHT-002/BHT-006 via zigbee2mqtt
+filter:TYPE=MQTT2_DEVICE:FILTER=CID~zigbee.* +order:L_17b1 +par:BASE_TOPIC;base topic set in configuration.yaml of the zigbee2mqtt bridge;{ AttrVal("DEVICE","devicetopic",AttrVal("DEVICE","readingList","")) =~ m,[\b]?([^/:]+)[/].+, ? $1 : undef } +par:DEV_ID;name of the device in the zigbee2mqtt bridge;{ AttrVal("DEVICE","devicetopic",AttrVal("DEVICE","readingList","")) =~ m,[^/]+[/]([^/:]+).*, ? $1 : undef } +par:ICON;ICON as set, defaults to temp_control;{ AttrVal("DEVICE","icon","temp_control") } +attr DEVICE icon ICON +attr DEVICE devicetopic BASE_TOPIC/DEV_ID +attr DEVICE readingList $\DEVICETOPIC:.* { json2nameValue($EVENT,'',$JSONMAP) } +attr DEVICE setList \ + desired-temp:slider,5.0,0.5,30.0,1 $\DEVICETOPIC/set {"current_heating_setpoint": $EVTPART1}\ + btnLock:LOCK,UNLOCK $\DEVICETOPIC/set {"child_lock": "$EVTPART1"}\ + mode:heat,off $\DEVICETOPIC/set {"system_mode": "$EVTPART1"}\ + preset:hold,program $\DEVICETOPIC/set {"preset": "$EVTPART1"}\ + saturday $\DEVICETOPIC/set/schedule { "saturday":[$EVTPART1] }\ + sunday $\DEVICETOPIC/set/schedule { "sunday":[$EVTPART1] }\ + weekdays $\DEVICETOPIC/set/schedule { "weekdays":[$EVTPART1] }\ + weekprofile { no strict 'vars';; FHEM::attrT_z2m_thermostat_Utils::z2t_send_BHT($NAME, $EVTPART1, $EVTPART2) }\ + x_send_set_payload:textField { my $payload = $EVENT;;$payload =~ s/$EVTPART0 //;; qq($\DEVICETOPIC/set $payload)} +attr DEVICE devStateIcon LOCKED:secur_lock:btnLock+UNLOCK UNLOCKED:secur_open:btnLock+LOCK +attr DEVICE webCmd desired-temp +attr DEVICE jsonMap current_heating_setpoint:desired-temp local_temperature:temperature Battery:batteryPercent system_mode:mode voltage:batterymV child_lock:btnLock +attr DEVICE stateFormat btnLock\ +Measured: temperature Battery: batteryPercent % +attr DEVICE userReadings batteryState:battery_low.* {ReadingsVal($name,'battery_low','false') eq 'false'?'ok':'low'}, batteryVoltage:batterymV.* {ReadingsNum($name,'batterymV',0)/1000} +attr DEVICE model zigbee2mqtt_thermostat_with_weekrofile_5_1_1 +set DEVICE attrTemplate speechcontrol_type_thermostat +deletereading -q DEVICE (?!associatedWith|IODev).* +setreading DEVICE attrTemplateVersion 20240402 + ########################################### # TASMOTA diff --git a/fhem/contrib/AttrTemplate/99_attrT_z2m_thermostat_Utils.pm b/fhem/contrib/AttrTemplate/99_attrT_z2m_thermostat_Utils.pm index b485279be..1a648df65 100644 --- a/fhem/contrib/AttrTemplate/99_attrT_z2m_thermostat_Utils.pm +++ b/fhem/contrib/AttrTemplate/99_attrT_z2m_thermostat_Utils.pm @@ -56,7 +56,7 @@ my %jsonmap = ( sub z2t_send_weekprofile { my $name = shift // carp q[No device name provided!] && return; my $wp_name = shift // carp q[No weekprofile device name provided!] && return; - my $wp_profile = shift // carp q[No weekprofile profile name provided!] && return; + my $wp_profile = shift // AttrVal($name, 'weekprofile', undef) // carp q[No weekprofile profile name provided!] && return; my $model = shift // ReadingsVal($name,'week','5+2'); my $topic = shift // AttrVal($name,'devicetopic','') . '/set'; @@ -111,7 +111,7 @@ sub z2t_send_weekprofile { sub z2t_send_Beca_weekprofile { my $name = shift // carp q[No device name provided!] && return; my $wp_name = shift // carp q[No weekprofile device name provided!] && return; - my $wp_profile = shift // carp q[No weekprofile profile name provided!] && return; + my $wp_profile = shift // AttrVal($name, 'weekprofile', undef) // carp q[No weekprofile profile name provided!] && return; my $topic = shift // carp q[No topic to send to provided!] && return; my $hash = $defs{$name}; @@ -151,6 +151,59 @@ sub z2t_send_Beca_weekprofile { return qq{$topic $payload}; } + +sub z2t_send_BHT { + my $name = shift // carp q[No device name provided!] && return; + my $wp_name = shift // carp q[No weekprofile device name provided!] && return; + my $wp_profile = shift // AttrVal($name, 'weekprofile', undef) // carp q[No weekprofile profile name provided!] && return; + my $topic = shift // AttrVal($name,'devicetopic','') . '/set'; + + my $hash = $defs{$name}; + + my $wp_profile_data = CommandGet(undef,"$wp_name profile_data $wp_profile 0"); + if ($wp_profile_data =~ m{(profile.*not.found|usage..profile_data..name)}xms ) { + Log3( $hash, 3, "[$name] weekprofile $wp_name: no profile named \"$wp_profile\" available" ); + return; + } + + my @D = qw(Sun Mon Tue Wed Thu Fri Sat); # eqals to my @D = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat"); + my $today = (localtime(time))[6]; + # if ( !($today ~~ [1..5]) ) {$today = 1}; + if ( !(0 < $today < 6) ) {$today = 1}; + Log3($hash, 3, "Fetching weekprofile for ${name} day ${today} / $D[${today}] from ${wp_name}/${wp_profile}"); + my @days = ($today,6,0); + my $payload; + my $decoded; + if ( !eval { $decoded = decode_json($wp_profile_data) ; 1 } ) { + Log3($name, 1, "JSON decoding error in $wp_profile provided by $wp_name: $@"); + return; + } + + for my $i (@days) { + my $sd = $i == 0 ? 'sunday' : $i == 6 ? 'saturday' : 'weekdays'; + + for my $j (0..3) { + my $time = $decoded->{$D[$i]}{'time'}[$j]; + last if !defined $time; + my ($hour,$minute) = split m{:}xms, $time; + $hour += 0; + $minute += 0; + my $tmp = $decoded->{$D[$i]}{'temp'}[$j]+0; + my $k = $j+1; + next if !looks_like_number($tmp); + $payload .= defined $payload ? ',' : '{'; + $payload .= qq("${sd}_p${k}_hour":$hour,"${sd}_p${k}_minute":$minute,"${sd}_p${k}_temperature":$tmp); + } + } + return if !defined $payload; + $payload = '{"program":'.$payload; + $payload .='}}'; + #Log3($hash,3,"Setting $name to new weekprofile: $payload"); + readingsSingleUpdate( $hash, 'weekprofile', "$wp_name $wp_profile",1); + return qq{$topic $payload}; +} + + 1; __END__