diff --git a/lib/mqtt.pm b/lib/mqtt.pm index 936ba430a..9fb80aabe 100644 --- a/lib/mqtt.pm +++ b/lib/mqtt.pm @@ -195,6 +195,7 @@ my $msg_id = 1; my $blocking_read_timeout = .5; my %MQTT_Data; +my $init_global_v_cmd = 0; # $main::Debug{mqtt} = 0; @@ -1049,17 +1050,24 @@ sub get_voice_cmds { #a bit of a kludge to pass along the voice command option, get the said value from the voice command. my $object_name = $self->get_object_name(); + my %global_voice_cmds = ( + " -- List all mqtt interfaces to the print log" => "&mqtt::print_interface_list()", + " -- Publish current states of all local items" => "&mqtt_LocalItem::publish_current_states()", + " -- Write all discovered items to /mqtt_discovered_items.mht.gen" => "&mqtt::write_discovered_items( '$::config_parms{data_dir}/mqtt_discovered_items.mht.gen' )", + ); my %voice_cmds = ( - "List all mqtt interfaces to the print log" => "${object_name}->print_interface_list()", - "List retained topics for $command" => "${object_name}->list_retained_topics()", - "Publish discovery data for $command" => "${object_name}->publish_discovery_data()", - "Publish current states of all local items" => "mqtt_LocalItem::publish_current_states()", - "Cleanup discovery info on $command and republish" => "${object_name}->cleanup_discovery_topics()", - "Cleanup all retained topics on mqtt server $command and republish (BE CAREFUL)" => "${object_name}->cleanup_all_retained_topics()", - "Write all discovered items to /mqtt_discovered_items.mht.gen" => "&mqtt::write_discovered_items( '$::config_parms{data_dir}/mqtt_discovered_items.mht.gen' )", + "$command -- List retained topics" => "${object_name}->list_retained_topics()", + "$command -- Publish discovery data" => "${object_name}->publish_discovery_data()", + "$command -- Publish current states of local items" => "${object_name}->publish_current_states()", + "$command -- Cleanup discovery info and republish" => "${object_name}->cleanup_discovery_topics()", + "$command -- Cleanup all retained topics on mqtt server and republish (BE CAREFUL)" => "${object_name}->cleanup_all_retained_topics()", # 'List [all,active,inactive] ' . $command . ' objects to the print log' => $self->get_object_name . '->print_object_list(SAID)', # "Print $objects $command attributes to the print log" => "${object_name}->print_object_attrs(SAID)", ); + if( $init_global_v_cmd == 0 ) { + $init_global_v_cmd = 1; + %voice_cmds = (%global_voice_cmds, %voice_cmds); + } return \%voice_cmds; } @@ -1070,13 +1078,12 @@ sub get_voice_cmds { =cut sub print_interface_list { - my ($self) = @_; my @interfaces; for my $inst (keys %MQTT_Data) { push @interfaces, $MQTT_Data{$inst}{self}->{instance}; } - $self->log( "MQTT interface list: " . join( ',', @interfaces ) ); + &mqtt::log( undef, "MQTT interface list: " . join( ',', @interfaces ) ); } =item C<(get_interface_list())> diff --git a/lib/mqtt_discovery.pm b/lib/mqtt_discovery.pm index 4800e498a..52c81976e 100644 --- a/lib/mqtt_discovery.pm +++ b/lib/mqtt_discovery.pm @@ -70,18 +70,30 @@ sub new { #### mqtt_DiscoveredItem my $self; my $short_disc_topic; my $mqtt_type; + my $disc_prefix; + my $disc_type; + my $node_id; + my $device_id; - my ($disc_prefix, $disc_type, $realm, $device_id) = $disc_topic =~ m|^(.*)/([^/]*)/([^/]+)/([^/]+)/config$|; - if( $disc_prefix ) { - $short_disc_topic = "$disc_type/$realm/$device_id/config"; - } else { - ($disc_prefix, $disc_type, $device_id) = $disc_topic =~ m|^(.*)/([^/]+)/([^/]+)/config$|; - if( !$disc_prefix ) { - $disc_obj->error( "UNRECOGNIZED DISCOVERY MESSAGE -- can't parse: $disc_topic" ); - return; + if( $disc_topic =~ m|.*/.*| ) { + ($disc_prefix, $disc_type, $node_id, $device_id) = $disc_topic =~ m|^(.*)/([^/]*)/([^/]+)/([^/]+)/config$|; + if( $disc_prefix ) { + $short_disc_topic = "$disc_type/$node_id/$device_id/config"; + } else { + ($disc_prefix, $disc_type, $device_id) = $disc_topic =~ m|^(.*)/([^/]+)/([^/]+)/config$|; + if( !$disc_prefix ) { + $disc_obj->error( "UNRECOGNIZED DISCOVERY MESSAGE -- can't parse: $disc_topic" ); + return; + } + $short_disc_topic = "$disc_type/$device_id/config"; } - $short_disc_topic = "$disc_type/$device_id/config"; + } elsif( $disc_topic ) { + $disc_type = $disc_topic; + $short_disc_topic = ''; + } else { + $disc_obj->error( "UNRECOGNIZED DISCOVERY MESSAGE -- discovery topic not valid: $disc_topic" ); + return; } my $disc_info; @@ -117,7 +129,7 @@ sub new { #### mqtt_DiscoveredItem } } if( $#listentopics < 0 ) { - $disc_obj->error( "UNRECOGNIZED DISCOVERY MESSAGE -- no topic: $disc_topic -- M:'$disc_msg" ); + $disc_obj->error( "UNRECOGNIZED DISCOVERY MESSAGE -- no listen topic: M:'$disc_msg" ); return; } @@ -129,12 +141,14 @@ sub new { #### mqtt_DiscoveredItem if( $obj->{is_local} ) { $disc_obj->debug( 1, "Ignoring discovery message received for local object: " . $obj->{local_item}->get_object_name ); } else { - if( $short_disc_topic eq $obj->{disc_topic} ) { + if( !$obj->{disc_topic} ) { + # object explicitly declared + $disc_obj->debug( 1, "Discovery message received for unique_id:($unique_id) for object that was explicitly created: " . $obj->get_object_name ); + } else { $disc_obj->debug( 1, "Discovery message received for unique_id:($unique_id) for object that already exists: " . $obj->get_object_name ); # Update the discover message so that if discovered items are written out, they get the new ones $obj->{disc_msg} = $disc_msg; - } else { - $disc_obj->error( "Discovery message received for unique_id:$unique_id, but topics don't match" ); + $obj->{disc_topic} = $short_disc_topic; } } $found = 1; @@ -146,9 +160,13 @@ sub new { #### mqtt_DiscoveredItem $attr = 'object_id'; } if( $obj->{disc_info}->{$attr} eq $disc_info->{$attr} ) { - # Note that Home Assistant matches discovery objects up based on friendly name -- report error on duplicate friendly names - $disc_obj->error( "Discovery message received for friendly_name ($disc_info->{$attr}) that already exists" ); $found = 1; + if( $obj->{discoverable} || $obj->{mqtt_dynamic} ) { + # Note that Home Assistant matches discovery objects up based on friendly name -- report error on duplicate friendly names + $disc_obj->error( "Discovery message received for friendly_name ($disc_info->{$attr}) that already exists" ); + } else { + $disc_obj->debug( 1, "Discovery message received for friendly_name ($disc_info->{$attr}) that has already been locally declared -- ignoring" ); + } } } } @@ -158,9 +176,9 @@ sub new { #### mqtt_DiscoveredItem # create local MH object $obj_name = 'mqttd_'; - if( $realm ) { - # $realm helps to identify the device - $obj_name .= "${realm}_"; + if( $node_id ) { + # $node_id helps to identify the device + $obj_name .= "${node_id}_"; } if( $disc_info->{name} ) { $obj_name .= $disc_info->{name}; @@ -188,7 +206,11 @@ sub new { #### mqtt_DiscoveredItem $self->{disc_prefix} = $disc_prefix; $self->{disc_msg} = $disc_msg; $self->{disc_obj} = $disc_obj; - $self->{discovered} = 1; + if( $short_disc_topic ) { + $self->{discovered} = 1; + } else { + $self->{discovered} = 0; + } $self->debug( 1, "New mqtt_DiscoveredItem( \$$disc_obj->{mqtt_name}, '$name', '$disc_topic', '$disc_msg' )" ); diff --git a/lib/mqtt_items.pm b/lib/mqtt_items.pm index edab92d38..30fdf71f8 100644 --- a/lib/mqtt_items.pm +++ b/lib/mqtt_items.pm @@ -105,7 +105,9 @@ Description: mqtt_InstMqttItem - implements a statically defined mh item for Insteon devices managed - by insteon-mqtt + by insteon-mqtt OR another instance of MisterHouse (since the MH published + MQTT messages are based on insteon-mqtt). You could also use discovered items + for this purpose. - see https://github.com/TD22057/insteon-mqtt - can publish HA discovery info, as insteon-mqtt does not implement discovery yet @@ -152,6 +154,12 @@ Description: - I have implemented these devices based on HomeAssistant documentation for discovery and information on blakaddr.com - In order to implement this properly, we would need a templating engine in perl + + You can move one of the items from the file written out by write_discovered_items, or + you could hand code an item of this type using a full discovery message. When you are + hand declaring an mqtt item using this format, change the discovery_topic to just + the discovery_type -- that will internally mark it so that it is not written out + next time write_discovered_items is called. mqtt_Discovery: This class has 2 functions. The discovery_action parameter defines which ones @@ -196,6 +204,7 @@ Usage: # MQTT_DISCOVERY, obj name, discovery prefix, broker, publish|subscribe|both (default both) MQTT_DISCOVERY, mqtt_discovery1, homeassistant, mqtt_1, both + ################################################### # Different Item types for different types of MQTT functionality # @@ -204,12 +213,15 @@ Usage: # - It helps identify your own discovery messages in a large mqtt system ################################################### - # Used to define mqtt items as published by insteon-mqtt project + # Used to define mqtt items as published by insteon-mqtt project or as published + # by another instance of MisterHouse which has defined MQTT_LOCALITEMs. + # # MQTT_INSTMQTT, name, groups, broker, type, topicpattern, discoverable Friendly Name MQTT_INSTMQTT, bootroom_switch, Lights, mqtt_1, switch, insteon/bootroom/+, 1, Bootroom Light # Define a Tasmota item. Note that the topicpattern must be in the order that the device will # send. This is configured in the Tasmota MQTT configuration. + # # MQTT_REMOTEITEM, name, groups, broker, type, topicpattern, discoverable Friendly Name MQTT_REMOTEITEM, tas_outdoor_plug, , mqtt_1, switch, tasmota_outdoor_plug/+/+, 0, Tasmota Outdoor Plug @@ -217,8 +229,10 @@ Usage: # Say you have a local INSTEON item (could be any kind of misterhouse item) INSTEON_SWITCHLINC, 52.9E.DD, shed_light, Lights|Outside # - # Then you can create an mqtt item to publish its state and receive mqtt commands + # Then you can create an mqtt item to publish its state and receive mqtt commands. # TopicPattern should be of the form "//+". + # *** This can be used to publish local MH items to Home Assistant. + # # MQTT_LOCALITEM, name, local item, broker, type, topicpattern, discoverable Friendly Name MQTT_LOCALITEM, bootroom_switch, shed_light, mqtt_1, switch, insteon/bootroom/+, 1, Bootroom Light # @@ -227,9 +241,17 @@ Usage: .mht generated file: - # Discovery items are generated by the write_discovered_items function - # You would not normally code these by hand - # MQTT_DISCOVEREDITEM, name, discovery_obj, discovery_topic, discovery_message + # Discovery items are generated by the write_discovered_items function. + # You would not normally code these by hand. + # + # But if you want to move an item to your regular .mht file, change the discovery_topic to just + # the discovery_type. That then will override any discovered item with the same unique_id, + # and will not be written out with write_discovered_items. + # + # You could code one of these items by hand. This would also allow you to declare a remote + # mqtt item using the full discovery message format. + # + # MQTT_DISCOVEREDITEM, name, discovery_obj, discovery_topic/discovery_type, discovery_message MQTT_DISCOVEREDITEM, mqtt_tasmota_outdoor_plug, mqtt_discovery, homeassistant/switch/877407_RL_1/config, {"name":"Tasmota Outside Plug","cmd_t":"~cmnd/POWER","stat_t":"~tele/STATE","val_tpl":"{{value_json.POWER}}","pl_off":"OFF","pl_on":"ON","avty_t":"~tele/LWT","pl_avail":"Online","pl_not_avail":"Offline","uniq_id":"877407_RL_1","device":{"identifiers":["877407"],"connections":[["mac","D8:F1:5B:87:74:07"]]},"~":"tasmota_outdoor_plug/"}