App-OpenHAP
view release on metacpan or search on metacpan
lib/App/OpenHAP/Tasmota/Device.pm view on Meta::CPAN
# ex:ts=8 sw=4:
# $OpenBSD$
#
# Copyright (c) 2025 Dick Olsson <hi@senzilla.io>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
use v5.36;
package App::OpenHAP::Tasmota::Device;
our $VERSION = '0.1.0';
use Fugu::Log;
require Protocol::HAP::Accessory;
our @ISA = qw(Protocol::HAP::Accessory);
use JSON::XS;
use constant {
# Device availability states
AVAILABILITY_UNKNOWN => 0,
AVAILABILITY_ONLINE => 1,
AVAILABILITY_OFFLINE => 2,
};
# The sensor types the walk below knows (H5). Thermostat and Sensor
# share this one list.
use constant SENSOR_TYPES =>
qw(DS18B20 DHT11 DHT22 AM2301 BME280 BMP280 SHT3X SI7021);
sub new ( $class, %args )
{
my $self = $class->SUPER::new(%args);
$self->{mqtt_topic} = $args{mqtt_topic};
$self->{mqtt_client} = $args{mqtt_client};
$self->{relay_index} = $args{relay_index} // 0; # 0 = no index
$self->{availability} = AVAILABILITY_UNKNOWN;
$self->{temp_unit} = 'C'; # Default to Celsius
return $self;
}
# $self->subscribe_mqtt():
# Subscribe to all standard Tasmota topics.
# Subclasses must call SUPER::subscribe_mqtt() first.
sub subscribe_mqtt ($self)
{
return unless $self->{mqtt_client}->is_connected();
Fugu::Log->default->debug(
'Tasmota %s subscribing to MQTT topics for %s',
ref($self), $self->{name} );
# C1: Subscribe to LWT for device availability
$self->{mqtt_client}->subscribe(
$self->_build_topic( 'tele', 'LWT' ),
sub ( $recv_topic, $payload ) {
$self->_handle_lwt($payload);
} );
# C2: Subscribe to tele/STATE for periodic state updates
$self->{mqtt_client}->subscribe(
$self->_build_topic( 'tele', 'STATE' ),
sub ( $recv_topic, $payload ) {
my $data = $self->_decode( $payload, 'STATE' )
or return;
$self->_process_state_data($data);
} );
# C3: Subscribe to stat/RESULT for command responses
$self->{mqtt_client}->subscribe(
$self->_build_topic( 'stat', 'RESULT' ),
sub ( $recv_topic, $payload ) {
my $data = $self->_decode( $payload, 'RESULT' )
or return;
$self->_process_result_data($data);
} );
# Subscribe to tele/SENSOR for sensor data
$self->{mqtt_client}->subscribe(
$self->_build_topic( 'tele', 'SENSOR' ),
sub ( $recv_topic, $payload ) {
my $data = $self->_decode( $payload, 'SENSOR' )
or return;
# Extract the temperature unit if it is present (H4)
if ( exists $data->{TempUnit} ) {
$self->{temp_unit} = $data->{TempUnit};
}
$self->_process_sensor_data($data);
} );
# C1/H1: Subscribe to STATUS11 for full state reconciliation
$self->{mqtt_client}->subscribe(
$self->_build_topic( 'stat', 'STATUS11' ),
sub ( $recv_topic, $payload ) {
$self->_handle_status11($payload);
} );
}
# $self->_subscribe_status_sns($status):
# Subscribe to one STATUS8/STATUS10 sensor response (H5). The
# device sends STATUS8 after an active query, and the spec
# recommends STATUS10. Both wrap the sensor data in StatusSNS.
( run in 3.162 seconds using v1.01-cache-2.11-cpan-54e63673c56 )