Skip to content

Commit

Permalink
Merge branch 'master' into Add_Kasa_Smart_support
Browse files Browse the repository at this point in the history
  • Loading branch information
hollie authored Dec 17, 2020
2 parents aa2fd64 + e78531f commit 6016b76
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ os:
dist: trusty

perl:
- "5.30"
- "5.24"
- "5.18"
- "5.16"
- "5.08"

install: true

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
MisterHouse
===========

[![Build Status](https://travis-ci.org/hollie/misterhouse.svg?branch=master)](https://travis-ci.org/hollie/misterhouse)
[![Build Status](https://travis-ci.com/hollie/misterhouse.svg?branch=master)](https://travis-ci.com/hollie/misterhouse)

Perl open source home automation program. It's fun, it's free, and it's entirely geeky.

Expand Down
170 changes: 170 additions & 0 deletions lib/Tasmota_HTTP_Item.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@

=begin comment
Tasmota_HTTP_Item.pm
Basic Tasmota support using the HTTP interface rather than MQTT
Copyright (C) 2020 Jeff Siddall ([email protected])
Last modified: 2020-12-15
This module currently supports Tasmota switch type devices but other devices
can be added with extra packages added
Requirements:
The Tasmota device needs to be setup with a rule to send HTTP requests to MH
if two-way communication is desired. For example, a Sonoff Mini switch input
can be sent to MH with the rule:
Rule1 ON Power1#State DO WebSend [192.168.0.1:80] /SET;none?select_item=Kitchen_Light&select_state=%value% ENDON
Setup:
In your code define Tasmota_HTTP::Things in an MHT:
TASMOTA_HTTP_SWITCH, 192.168.x.y, Kitchen_Light, Kitchen
Or in a code file:
$Kitchen_Light = new Tasmota_HTTP::Switch("192.168.x.y");
Where:
192.168.x.y is the IPv4 address or hostname of the Tasmota device
$Kitchen_Light->set(ON);
=cut

#=======================================================================================
#
# Generic Tasmota_HTTP::Item
#
#=======================================================================================

# The Tasmota_HTTP::Item is a base item for other real devices (see below)

package Tasmota_HTTP::Item;
use strict;
use parent 'Generic_Item';

# Item class constructor
sub new {
my ( $class, $address ) = @_;

# Call the parent class constructor to make sure all the important things are done
my $self = new Generic_Item();
bless $self, $class;

# Additional Tasmota variables
$self->{address} = $address;
$self->{output_name} = 'POWER1';
$self->{ack} = 0;
$self->{last_http_status};

return $self;
}

# Use HTTP get calls to set the Tasmota item, being sure to check that the set did not come
# from the device itself
sub set {
my ( $self, $state, $set_by, $respond ) = @_;

# Debug logging
my $debug = $self->{debug} || $main::Debug{tasmota};

# Determine whether the update came from the Tasmota device itself and convert states
# and record the set as an ack
if ( $set_by eq "web [$self->{address}]" ) {

# Convert Tasmota states to MH states
$state = $self->{tasmota_to_state}{$state};

# If the current state is the same as the received state, and ack=0 then consider
# this set an ack and do not update the state of the item
if ( ( $state eq $self->{state} ) && ( $self->{ack} == 0 ) ) {
&main::print_log("[Tasmota_HTTP::Item] DEBUG: Received ack from $self->{object_name} ($self->{address})") if $debug;
$self->{ack} = 1;
}
else {
&main::print_log("[Tasmota_HTTP::Item] DEBUG: Received set state to $state from $self->{object_name} ($self->{address})") if $debug;

# Call the parent class set to make sure all the important things are done
$self->SUPER::set( $state, $set_by, $respond );
}

# Only send an update to the device if the set did not come from the device to prevent
# set loops
}
else {
use LWP::UserAgent ();

# Use a small timeout since devices are typically local and should respond quickly
# 5 seconds should allow for 3 syn attempts plus another second to get a response
my $ua = LWP::UserAgent->new( timeout => 5 );

# Reset the ack flag
$self->{ack} = 0;

# Send the HTTP request
my $response = $ua->get("http://$self->{address}/cm?cmnd=$self->{output_name}%20$self->{state_to_tasmota}{$state}");

# Record the status of the last request
$self->{last_http_status} = $response->status_line;

# Log request failures
if ( !$response->is_success ) {
&main::print_log("[Tasmota_HTTP::Item] ERROR: Received HTTP response code $self->{last_http_status} from last command)");
}

# Call the parent class set to make sure all the important things are done
$self->SUPER::set( $state, $set_by, $respond );
&main::print_log("[Tasmota_HTTP::Item] DEBUG: Set $self->{object_name} state to $state") if $debug;
}
}

#=======================================================================================
#
# Basic Tasmota_HTTP::Switch
#
#=======================================================================================

# To add table support, add these lines to the read_table_A.pl file:
# elsif ( $type eq "TASMOTA_HTTP_SWITCH" ) {
# require Tasmota_HTTP_Item;
# ( $address, $name, $grouplist ) = @item_info;
# $object = "Tasmota_HTTP::Switch('$address')";
# }

package Tasmota_HTTP::Switch;
use strict;
use parent-norequire, 'Tasmota_HTTP::Item';

# Switch class constructor
sub new {
my $class = shift;

# Call the parent class constructor to make sure all the important things are done
my $self = $class->SUPER::new(@_);

# Additional switch variables
# Add additional hash pairs (rows) to this variable to send other states to devices
$self->{state_to_tasmota} = {
"off" => "0",
"on" => "1",
};

# Add additional hash pairs (rows) to this variable to use other states from devices
$self->{tasmota_to_state} = {
"0" => "off",
"1" => "on",
};

# Initialize states
push( @{ $self->{states} }, keys( %{ $self->{state_to_tasmota} } ) );

# Log the setup of the item
&main::print_log("[Tasmota_HTTP::Switch] Created item with address $self->{address}");

return $self;
}

# Perl modules need to return true
1;
39 changes: 38 additions & 1 deletion lib/read_table_A.pl
Original file line number Diff line number Diff line change
Expand Up @@ -1901,7 +1901,39 @@ sub read_table_A {
$object = '';
}
#-------------- End AoGSmartHome Objects ----------------

#-------------- MQTT Objects -----------------
elsif ( $type eq "MQTT_BROKER" ) {
# there must be one record for the broker above any MQTT_DEVICE definitions
# it takes the following format
# MQTT_BROKER, name_of_broker
# e.g.MQTT_BROKER, mqtt_1
require 'mqtt.pm';
( $name ) = @item_info;
$code .= sprintf( "\n\$%-35s = new mqtt(\"%s\", \$config_parms{mqtt_host},
\$config_parms{mqtt_server_port},
\$config_parms{mqtt_topic},
\$config_parms{mqtt_username},
\$config_parms{mqtt_password}, 121);\n",
$name,
$name
);
}
elsif ( $type eq "MQTT_DEVICE" ) {
# there is one record per mqtt device and it must be below the MQTT_BROKER definition
# it takes the following form
# MQTT_DEVICE, name_of_device, groups, name_of_broker, topic
# e.g. MQTT_DEVICE, MQTT_test, Kitchen, mqtt_1, stat/mh_mqtt_test/SENSOR
# if the device is to transmit to MH, its topic must match the
# config parameter mqtt_topic in the mh.ini file
require 'mqtt.pm';
my ($MQTT_broker_name, $MQTT_topic);
( $name, $grouplist, $MQTT_broker_name, $MQTT_topic ) = @item_info;

$code .= sprintf( "\n\$%-35s = new mqtt_Item(\$%s\,\"%s\");\n",
$name, $MQTT_broker_name, $MQTT_topic );

}
#-------------- End MQTT Objects ----------------
elsif ( $type =~ /PLCBUS_.*/ ) {
#<,PLCBUS_Scene,Address,Name,Groups,Default|Scenes>#
require PLCBUS;
Expand Down Expand Up @@ -1929,6 +1961,11 @@ sub read_table_A {
$object = "Kasa_Item('$address', '$type', $index)";
}
}
elsif ( $type eq "TASMOTA_HTTP_SWITCH" ) {
require Tasmota_HTTP_Item;
( $address, $name, $grouplist ) = @item_info;
$object = "Tasmota_HTTP::Switch('$address')";
}
else {
print "\nUnrecognized .mht entry: $record\n";
return;
Expand Down
3 changes: 3 additions & 0 deletions lib/xAP_Items.pm
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,9 @@ sub _process_incoming_xap_data {
if $o->allow_empty_state()
or ( defined $state_value and $state_value ne '' );
}
} else {
print "db1 xap discarded, source is mh: s=$source d=$data\n"
if $main::Debug{xap} and $main::Debug{xap} == 1;
}
}

Expand Down

0 comments on commit 6016b76

Please sign in to comment.