From 7a3c3163c59d4571636e689855b6df1b2b408866 Mon Sep 17 00:00:00 2001 From: DaveNeudoerffer Date: Sat, 7 Sep 2024 09:10:05 -0400 Subject: [PATCH 1/4] HPlato: added fan support --- lib/HA_Item.pm | 61 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/lib/HA_Item.pm b/lib/HA_Item.pm index 6e677430e..77d67071e 100644 --- a/lib/HA_Item.pm +++ b/lib/HA_Item.pm @@ -76,6 +76,7 @@ Description: - light: on, off and brightness :rgb_color : for setting an RGB value :effect : for setting a lighting effect + - fan: on, off and speed (%) - cover: open,stop,close :digital : for allowing granular setpoints - lock: lock, unlock @@ -114,7 +115,10 @@ Description: - ha_call_service also exists on the HA_Server class - this can be used to call services that are not entity services eg. $hasrv->ha_call_service( 'notify.mobile_app_my_phone', {title=>'HA notification', message=>'test message'} ); - + - or add this to items.mht for controlling HA services, like an RF fan controlled by a broadlink item: + GENERIC, fan_light, + CODE, $fan_light -> tie_event('$hasrv->ha_call_service( "remote.rm4pro.send_command", {device => "Fan", command=> "Light"})'); + CODE, $fan_light -> set_states ("on","off"); Discovery: @@ -355,7 +359,6 @@ sub new { $self->log( "creating HA Server $name on $address" ); &::MainLoop_pre_add_hook( \&HA_Server::check_for_data, 1, $self ); - # &::Reload_pre_add_hook( \&HA_Server::disconnect, 1, $self ); &::Reload_post_add_hook( \&HA_Server::generate_voice_commands, 1, $self ); $HA_Server_List{$name} = $self; @@ -1044,6 +1047,8 @@ sub new { } } elsif( $domain eq 'lock' ) { $self->set_states( "unlocked", "locked" ); + } elsif( $domain eq 'fan' ) { + # placeholder in case we need to do something. States are set dynamically when the object is set } elsif( $domain eq 'climate' ) { } elsif( $domain eq 'sensor' || $domain eq 'binary_sensor' ) { } elsif( $domain eq 'select' || $domain eq 'input_select' ) { @@ -1213,15 +1218,38 @@ sub process_ha_message { if( $p_setby eq 'ha_server_init' ) { $self->set_states( @{$new_state->{attributes}->{options}},"override=1" ); } + } elsif( $self->{domain} eq 'fan' ) { + my $level = $new_state->{state}; + if( $new_state->{state} eq 'on' ){ + $level = $new_state->{attributes}->{percentage} . "%" if( $new_state->{attributes}->{percentage} ); + $level = "off" if ($level eq "0%"); + } + $self->debug( 1, "fan event for $self->{object_name} set to $new_state->{state} ($level)" ); + $self->SUPER::set( $level, $p_setby, $p_response ); + if( $p_setby eq 'ha_server_init' ) { + #percentage_step gives the number of speed steps for the fan + if (defined $new_state->{attributes}->{percentage_step}) { + my @states = (); + push @states, "off"; + for ($i = $new_state->{attributes}->{percentage_step}; $i < 100; $i = $i + $new_state->{attributes}->{percentage_step}) { + next if ($i >96); + push @states,int($i) . "%"; + } + push @states, "100%","on"; + $self->set_states( @states,"override=1" ); + } else { + $self->set_states( "on", "off","override=1" ); + } + } } elsif( $self->{domain} eq 'light' ) { if (lc $self->{subtype} eq "rgb_color") { if( $new_state->{attributes} && ref $new_state->{attributes}->{$self->{subtype}} ) { - #shouldn't join, but rgb is an array so for now create a string - my $string = join ',', @{$new_state->{attributes}->{ $self->{subtype} }}; - $self->debug( 1, "handled subtype $self->{subtype} event for $self->{object_name} set to $string" ); - $self->SUPER::set( $string, $p_setby, $p_response ); + #shouldn't join, but rgb is an array so for now create a string + my $string = join ',', @{$new_state->{attributes}->{ $self->{subtype} }}; + $self->debug( 1, "handled subtype $self->{subtype} event for $self->{object_name} set to $string" ); + $self->SUPER::set( $string, $p_setby, $p_response ); } else { - $self->debug( 1, "got light state for $self->{object_name} but no rgb_color attribute" ); + $self->debug( 1, "got light state for $self->{object_name} but no rgb_color attribute" ); } } elsif (lc $self->{subtype} eq "effect") { # update the set_states based on the effects_list array @@ -1233,9 +1261,9 @@ sub process_ha_message { } else { my $level = $new_state->{state}; if( $new_state->{state} eq 'on' ){ - if( $new_state->{attributes}->{brightness} ) { - $level = int( $new_state->{attributes}->{brightness} * 100 / 255 + .5); - } + if( $new_state->{attributes}->{brightness} ) { + $level = int( $new_state->{attributes}->{brightness} * 100 / 255 + .5); + } } $self->debug( 1, "light event for $self->{object_name} set to $level" ); $self->SUPER::set( $level, $p_setby, $p_response ); @@ -1244,7 +1272,7 @@ sub process_ha_message { my $state; foreach my $attrname (keys %{$new_state->{attributes}} ) { if( $self->{subtype} eq $attrname ) { - $state = $new_state->{attributes}->{$attrname}; + $state = $new_state->{attributes}->{$attrname}; } } #Check for duplicates again as the $new state is inside the attributes @@ -1267,11 +1295,11 @@ sub process_ha_message { } if( $p_setby eq 'ha_server_init' ) { if( $self->{subtype} eq 'hvac_mode' ) { - $self->set_states( @{$new_state->{attributes}->{hvac_modes}},"override=1" ); + $self->set_states( @{$new_state->{attributes}->{hvac_modes}},"override=1" ); } elsif( $self->{subtype} eq 'fan_mode' ) { - $self->set_states( @{$new_state->{attributes}->{fan_modes}},"override=1" ); + $self->set_states( @{$new_state->{attributes}->{fan_modes}},"override=1" ); } elsif( $self->{subtype} eq 'preset_mode' ) { - $self->set_states( @{$new_state->{attributes}->{preset_modes}},"override=1" ); + $self->set_states( @{$new_state->{attributes}->{preset_modes}},"override=1" ); } } $self->SUPER::set( $state, $p_setby, $p_response ); @@ -1414,7 +1442,10 @@ sub ha_set_state { } elsif (lc $self->{domain} eq 'cover') { $service = 'set_cover_position'; $service_data->{position} = $numval; - } elsif ( lc $self->{domain} eq 'number' || lc $self->{domain} eq 'input_number' ) { + } elsif (lc $self->{domain} eq 'fan') { + $service = 'set_percentage'; + $service_data->{percentage} = $numval; + } elsif ( lc $self->{domain} eq 'number' || lc $self->{domain} eq 'input_number' ) { $service = 'set_value'; $service_data->{value} = $numval; } else { From 7b8dc4268d01c497d521ed74b9fd998d333de077 Mon Sep 17 00:00:00 2001 From: DaveNeudoerffer Date: Sat, 7 Sep 2024 09:25:04 -0400 Subject: [PATCH 2/4] more tab fixes --- lib/HA_Item.pm | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/HA_Item.pm b/lib/HA_Item.pm index 77d67071e..56b9d4fb7 100644 --- a/lib/HA_Item.pm +++ b/lib/HA_Item.pm @@ -1221,8 +1221,8 @@ sub process_ha_message { } elsif( $self->{domain} eq 'fan' ) { my $level = $new_state->{state}; if( $new_state->{state} eq 'on' ){ - $level = $new_state->{attributes}->{percentage} . "%" if( $new_state->{attributes}->{percentage} ); - $level = "off" if ($level eq "0%"); + $level = $new_state->{attributes}->{percentage} . "%" if( $new_state->{attributes}->{percentage} ); + $level = "off" if ($level eq "0%"); } $self->debug( 1, "fan event for $self->{object_name} set to $new_state->{state} ($level)" ); $self->SUPER::set( $level, $p_setby, $p_response ); @@ -1238,18 +1238,18 @@ sub process_ha_message { push @states, "100%","on"; $self->set_states( @states,"override=1" ); } else { - $self->set_states( "on", "off","override=1" ); + $self->set_states( "on", "off","override=1" ); } } } elsif( $self->{domain} eq 'light' ) { if (lc $self->{subtype} eq "rgb_color") { if( $new_state->{attributes} && ref $new_state->{attributes}->{$self->{subtype}} ) { - #shouldn't join, but rgb is an array so for now create a string - my $string = join ',', @{$new_state->{attributes}->{ $self->{subtype} }}; - $self->debug( 1, "handled subtype $self->{subtype} event for $self->{object_name} set to $string" ); - $self->SUPER::set( $string, $p_setby, $p_response ); + #shouldn't join, but rgb is an array so for now create a string + my $string = join ',', @{$new_state->{attributes}->{ $self->{subtype} }}; + $self->debug( 1, "handled subtype $self->{subtype} event for $self->{object_name} set to $string" ); + $self->SUPER::set( $string, $p_setby, $p_response ); } else { - $self->debug( 1, "got light state for $self->{object_name} but no rgb_color attribute" ); + $self->debug( 1, "got light state for $self->{object_name} but no rgb_color attribute" ); } } elsif (lc $self->{subtype} eq "effect") { # update the set_states based on the effects_list array @@ -1261,9 +1261,9 @@ sub process_ha_message { } else { my $level = $new_state->{state}; if( $new_state->{state} eq 'on' ){ - if( $new_state->{attributes}->{brightness} ) { - $level = int( $new_state->{attributes}->{brightness} * 100 / 255 + .5); - } + if( $new_state->{attributes}->{brightness} ) { + $level = int( $new_state->{attributes}->{brightness} * 100 / 255 + .5); + } } $self->debug( 1, "light event for $self->{object_name} set to $level" ); $self->SUPER::set( $level, $p_setby, $p_response ); From 2649325af8b1b79fdb7c7bd8a4b6a1c80a2bc4a7 Mon Sep 17 00:00:00 2001 From: DaveNeudoerffer Date: Sat, 7 Sep 2024 10:42:21 -0400 Subject: [PATCH 3/4] don't set ha init state if it is the same as MH --- lib/HA_Item.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/HA_Item.pm b/lib/HA_Item.pm index 56b9d4fb7..70065f104 100644 --- a/lib/HA_Item.pm +++ b/lib/HA_Item.pm @@ -1190,6 +1190,10 @@ sub process_ha_message { $self->debug( 2, "Duplicate state $new_state->{state} ignored on $self->{object_name}" ); return; } + if( ( $p_setby eq 'ha_server_init' ) and ( lc( $self->state() ) eq lc( $new_state->{state} )) ) { + $self->debug( 2, "ha_server_init: Duplicate state $new_state->{state} ignored on $self->{object_name}" ); + return; + } if( $self->{domain} eq 'switch' || $self->{domain} eq 'lock' From 35fca51d53e070a4ee26a552e19e8d1f9a51ede9 Mon Sep 17 00:00:00 2001 From: DaveNeudoerffer Date: Sun, 8 Sep 2024 09:49:42 -0400 Subject: [PATCH 4/4] fixed use of unitialized variable --- lib/HA_Item.pm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/HA_Item.pm b/lib/HA_Item.pm index 70065f104..37d5bf934 100644 --- a/lib/HA_Item.pm +++ b/lib/HA_Item.pm @@ -1190,9 +1190,11 @@ sub process_ha_message { $self->debug( 2, "Duplicate state $new_state->{state} ignored on $self->{object_name}" ); return; } - if( ( $p_setby eq 'ha_server_init' ) and ( lc( $self->state() ) eq lc( $new_state->{state} )) ) { - $self->debug( 2, "ha_server_init: Duplicate state $new_state->{state} ignored on $self->{object_name}" ); - return; + if( $p_setby eq 'ha_server_init' ) { + if( defined $self->state() && lc( $self->state() ) eq lc( $new_state->{state} ) ) { + $self->debug( 2, "ha_server_init: Duplicate state $new_state->{state} ignored on $self->{object_name}" ); + return; + } } if( $self->{domain} eq 'switch'