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 )