defmod AbfallAusloeser at *06:00:00 set Abfall update Zitat von: DS_Starter am 01 Januar 2026, 18:32:56Im contrib befindet sich ein Update der 2.0.0.
Hauptaugenmerk liegt auf FANN.
The key 'aiConHiddenLayers=80‑40‑20' is not specified correctly. Please refer to the command reference.hab den Eintrag erst einmal außen vor gelassen.aiTrainStart=3
aiStorageDuration=3600
aiTreesPV=30
aiConActivate=1
aiConAlpha=1
aiConHiddenLayers=80‑40‑20
aiConTrainStart=3:2
aiConActFunc=GAUSSIAN
aiConLearnRate=0.001
aiConMomentum=0.7
aiConShuffleMode=1
aiConShufflePeriod=10
aiConSteepness=0.9
aiConTrainAlgo=RPROP
Zitatsag mal, (wie) kann man denn mit einer Instanz von SolarForecast zwischen der alten und der neuen KI-basierten Verbrauchsprognose wechseln - oder macht das keinen Sinn?Ja, kann man und macht durchaus Sinn (vor allem in der Test/Findungsphase) bzw. ist nicht schädlich.
attr <Name> aiControl aiConActivate=1
nach attr <Name> aiControl aiConActivate=2attr <Name> aiControl aiConAlpha <Wert> my $display = join(", ",@morgen);;\
my $pushmsg = 'Morgen: '.$display;;\
fhem("attr -silent Abfall stateFormat <span style='color:#cc0000'>$display</span>");;\
fhem("attr -silent Abfall room Kalender");;;;fhem("set pushmsg msg 🚮: $pushmsg");;\
Shelly_EG_3 type=heater power=3000 icon=IR pcurr=power:W etotal=energy:Wh mode=can mintime=SunPath on=on off=off interruptable=1 swoncond=calcEnergieManager:sf_true:{main::Check_swoncond}sub
Check_swoncond{
my $consumer_id = sprintf "%02d", 1;
my $nom_power = FHEM::SolarForecast::ConsumerVal("SF01", $consumer_id, 'power', '');
my $SF01_Current_Surplus = ReadingsVal("SF01","Current_Surplus",0);
($SF01_Current_Surplus) = split(/\s+/, $SF01_Current_Surplus);
my $SF01_BatIn = ReadingsVal("SF01","Current_PowerBatIn_01",0);
($SF01_BatIn) = split(/\s+/, $SF01_BatIn);
my $surplusMinBat = $SF01_Current_Surplus - $SF01_BatIn;
if($surplusMinBat>$nom_power) {
Log 3, "Check_swoncond: surplusMinBat=$surplusMinBat -> return 1";
return 1;}
else {
Log 3, "check swoncond Surplus: $SF01_Current_Surplus BatIn $SF01_BatIn surplusMinBat: $surplusMinBat -> return 0 nomPower: $nom_power";
return 0;}
}Zitat von: DS_Starter am 01 Januar 2026, 18:32:56Zur Zeit fehlt zum Bsp. der Anwesenheitsindikator der aktuell 0 sein müsste und demzufolge weniger Verbrauch indiziert wäre.

defmod Handy_XY PRESENCE function {checkAllFritzMACpresent("xy:xy:xy:xy:xy:xy")} 60 60
attr Handy_XY event-min-interval presence:900
attr Handy_XY event-on-change-reading presence
attr Handy_XY group Anwesenheit
attr Handy_XY room Presence##############################################
# $Id: 98_IntesisWMP.pm 2025-01-01 $
#
# FHEM Module for Intesis WMP Protocol (PA-AC-WMP-1 / INWMPPAN001I000)
# Controls air conditioning devices via TCP/ASCII protocol on port 3310
#
# Based on WMP Protocol Specification V1.11
#
package main;
use strict;
use warnings;
use IO::Socket::INET;
use Time::HiRes qw(gettimeofday);
# Module version
my $IntesisWMP_Version = "1.2.2";
# Constants
my $WMP_PORT = 3310;
my $WMP_TIMEOUT = 5;
my $WMP_KEEPALIVE_INTERVAL = 30; # seconds between pings (must be < 60s)
my $WMP_UPDATE_INTERVAL = 60; # seconds between full updates
##############################################
# Initialize module
##############################################
sub IntesisWMP_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = "IntesisWMP_Define";
$hash->{UndefFn} = "IntesisWMP_Undef";
$hash->{SetFn} = "IntesisWMP_Set";
$hash->{GetFn} = "IntesisWMP_Get";
$hash->{ReadFn} = "IntesisWMP_Read";
$hash->{ReadyFn} = "IntesisWMP_Ready";
$hash->{AttrFn} = "IntesisWMP_Attr";
$hash->{NotifyFn} = "IntesisWMP_Notify";
$hash->{AttrList} =
"disable:0,1 " .
"interval " .
"updateInterval " .
"timeout " .
"username " .
"password " .
"unit " .
"tempUnit:C,F " .
"pollFunctions " .
$readingFnAttributes;
Log3 undef, 3, "IntesisWMP: Module initialized (Version $IntesisWMP_Version)";
}
##############################################
# Define device
##############################################
sub IntesisWMP_Define($$) {
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
return "Usage: define <name> IntesisWMP <host> [<port>]" if(@a < 3);
my $name = $a[0];
my $host = $a[2];
my $port = $a[3] || $WMP_PORT;
$hash->{HOST} = $host;
$hash->{PORT} = $port;
$hash->{UNIT} = 1; # Default unit number
$hash->{INTERVAL} = $WMP_KEEPALIVE_INTERVAL;
$hash->{UPDATE_INTERVAL} = $WMP_UPDATE_INTERVAL;
$hash->{TIMEOUT} = $WMP_TIMEOUT;
$hash->{VERSION} = $IntesisWMP_Version;
$hash->{NOTIFYDEV} = "global";
# Initialize state
$hash->{STATE} = "Initialized";
$hash->{LAST_RECV} = 0;
$hash->{LAST_SEND} = 0;
$hash->{LAST_UPDATE} = 0;
$hash->{CONNECTED} = 0;
$hash->{BUFFER} = "";
# Valid values for readings/settings
$hash->{helper}{validModes} = {
AUTO => 1, HEAT => 1, DRY => 1, FAN => 1, COOL => 1
};
$hash->{helper}{validFanSpeeds} = {
AUTO => 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1,
6 => 1, 7 => 1, 8 => 1, 9 => 1
};
$hash->{helper}{validVanes} = {
AUTO => 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1,
6 => 1, 7 => 1, 8 => 1, 9 => 1, SWING => 1
};
# Default poll functions
$hash->{helper}{pollFunctions} = [qw(ONOFF MODE SETPTEMP AMBTEMP FANSP VANEUD VANELR)];
Log3 $name, 3, "IntesisWMP ($name): Defined with host $host:$port";
# Start connection after FHEM is initialized
if($init_done) {
IntesisWMP_Connect($hash);
}
return undef;
}
##############################################
# Handle FHEM notifications (global events)
##############################################
sub IntesisWMP_Notify($$) {
my ($hash, $dev) = @_;
my $name = $hash->{NAME};
return if(AttrVal($name, "disable", 0));
if($dev->{NAME} eq "global") {
my $events = deviceEvents($dev, 1);
return if(!$events);
foreach my $event (@{$events}) {
if($event =~ /^INITIALIZED$/) {
Log3 $name, 4, "IntesisWMP ($name): FHEM initialized, connecting...";
IntesisWMP_Connect($hash);
}
}
}
return undef;
}
##############################################
# Connect to the Intesis device
##############################################
sub IntesisWMP_Connect($) {
my ($hash) = @_;
my $name = $hash->{NAME};
return if(AttrVal($name, "disable", 0));
# Close existing connection if any
IntesisWMP_Disconnect($hash);
my $host = $hash->{HOST};
my $port = $hash->{PORT};
my $timeout = $hash->{TIMEOUT};
Log3 $name, 3, "IntesisWMP ($name): Connecting to $host:$port...";
# Create non-blocking connection
my $conn = IO::Socket::INET->new(
PeerAddr => $host,
PeerPort => $port,
Proto => 'tcp',
Timeout => $timeout,
Blocking => 0
);
if(!$conn) {
Log3 $name, 2, "IntesisWMP ($name): Connection failed: $!";
$hash->{STATE} = "disconnected";
$hash->{CONNECTED} = 0;
readingsSingleUpdate($hash, "state", "disconnected", 1);
# Schedule reconnect
RemoveInternalTimer($hash, "IntesisWMP_Connect");
InternalTimer(gettimeofday() + 60, "IntesisWMP_Connect", $hash, 0);
return;
}
# Store connection handle
$hash->{FD} = $conn->fileno();
$hash->{CD} = $conn;
$hash->{CONNECTED} = 1;
$hash->{STATE} = "connected";
$hash->{BUFFER} = "";
$hash->{LAST_RECV} = gettimeofday();
$selectlist{$name} = $hash;
Log3 $name, 3, "IntesisWMP ($name): Connected to $host:$port";
readingsSingleUpdate($hash, "state", "connected", 1);
# Login if credentials are set
my $username = AttrVal($name, "username", "");
my $password = AttrVal($name, "password", "");
if($username && $password) {
IntesisWMP_Login($hash, $username, $password);
}
# Request device info
IntesisWMP_Send($hash, "ID");
# Start keepalive timer
IntesisWMP_StartKeepaliveTimer($hash);
# Start update timer (initial update after 2 seconds)
IntesisWMP_StartUpdateTimer($hash, 2);
return;
}
##############################################
# Start keepalive timer
##############################################
sub IntesisWMP_StartKeepaliveTimer($) {
my ($hash) = @_;
my $name = $hash->{NAME};
RemoveInternalTimer($hash, "IntesisWMP_Keepalive");
my $interval = AttrVal($name, "interval", $hash->{INTERVAL});
Log3 $name, 4, "IntesisWMP ($name): Starting keepalive timer (interval: ${interval}s)";
InternalTimer(gettimeofday() + $interval, "IntesisWMP_Keepalive", $hash, 0);
}
##############################################
# Start update timer
##############################################
sub IntesisWMP_StartUpdateTimer($;$) {
my ($hash, $delay) = @_;
my $name = $hash->{NAME};
RemoveInternalTimer($hash, "IntesisWMP_UpdateTimer");
my $interval = AttrVal($name, "updateInterval", $hash->{UPDATE_INTERVAL});
$delay = $interval if(!defined($delay));
Log3 $name, 4, "IntesisWMP ($name): Starting update timer (delay: ${delay}s, interval: ${interval}s)";
InternalTimer(gettimeofday() + $delay, "IntesisWMP_UpdateTimer", $hash, 0);
}
##############################################
# Disconnect from device
##############################################
sub IntesisWMP_Disconnect($) {
my ($hash) = @_;
my $name = $hash->{NAME};
Log3 $name, 4, "IntesisWMP ($name): Disconnecting, removing all timers...";
RemoveInternalTimer($hash, "IntesisWMP_Keepalive");
RemoveInternalTimer($hash, "IntesisWMP_Connect");
RemoveInternalTimer($hash, "IntesisWMP_UpdateTimer");
# Remove staggered poll timers
foreach my $func (qw(ONOFF MODE SETPTEMP AMBTEMP FANSP VANEUD VANELR ERRSTATUS ERRCODE)) {
RemoveInternalTimer("$name:poll:$func");
}
if($hash->{CD}) {
Log3 $name, 4, "IntesisWMP ($name): Closing connection...";
delete $selectlist{$name};
close($hash->{CD});
delete $hash->{CD};
delete $hash->{FD};
}
$hash->{CONNECTED} = 0;
$hash->{STATE} = "disconnected";
return;
}
##############################################
# Undefine device
##############################################
sub IntesisWMP_Undef($$) {
my ($hash, $arg) = @_;
my $name = $hash->{NAME};
IntesisWMP_Disconnect($hash);
Log3 $name, 3, "IntesisWMP ($name): Device undefined";
return undef;
}
##############################################
# Send command to device
##############################################
sub IntesisWMP_Send($$) {
my ($hash, $cmd) = @_;
my $name = $hash->{NAME};
if(!$hash->{CD} || !$hash->{CONNECTED}) {
Log3 $name, 3, "IntesisWMP ($name): Not connected, cannot send: $cmd";
IntesisWMP_Connect($hash);
return "Not connected";
}
my $msg = $cmd . "\r\n";
Log3 $name, 4, "IntesisWMP ($name): Sending: $cmd";
my $written = syswrite($hash->{CD}, $msg);
if(!defined($written)) {
Log3 $name, 2, "IntesisWMP ($name): Write error: $!";
IntesisWMP_Disconnect($hash);
readingsSingleUpdate($hash, "state", "disconnected", 1);
IntesisWMP_Connect($hash);
return "Write error";
}
$hash->{LAST_SEND} = gettimeofday();
return undef;
}
##############################################
# Read data from device (called by select loop)
##############################################
sub IntesisWMP_Read($) {
my ($hash) = @_;
my $name = $hash->{NAME};
my $buf;
my $len = sysread($hash->{CD}, $buf, 1024);
if(!defined($len) || $len <= 0) {
Log3 $name, 3, "IntesisWMP ($name): Connection closed by remote";
IntesisWMP_Disconnect($hash);
readingsSingleUpdate($hash, "state", "disconnected", 1);
# Schedule reconnect
InternalTimer(gettimeofday() + 10, "IntesisWMP_Connect", $hash, 0);
return;
}
$hash->{LAST_RECV} = gettimeofday();
$hash->{BUFFER} .= $buf;
# Process complete lines
while($hash->{BUFFER} =~ s/^([^\r\n]*)\r?\n//) {
my $line = $1;
next if($line eq "");
Log3 $name, 4, "IntesisWMP ($name): Received: $line";
IntesisWMP_Parse($hash, $line);
}
return;
}
##############################################
# Ready function for reconnect
##############################################
sub IntesisWMP_Ready($) {
my ($hash) = @_;
my $name = $hash->{NAME};
return if(AttrVal($name, "disable", 0));
if(!$hash->{CD} || !$hash->{CONNECTED}) {
return IntesisWMP_Connect($hash);
}
return 0;
}
##############################################
# Parse received data
##############################################
sub IntesisWMP_Parse($$) {
my ($hash, $line) = @_;
my $name = $hash->{NAME};
$line = uc($line); # Protocol is case-insensitive
# ACK response
if($line eq "ACK") {
Log3 $name, 5, "IntesisWMP ($name): Command acknowledged";
return;
}
# ERR response
if($line eq "ERR") {
Log3 $name, 3, "IntesisWMP ($name): Error response received";
readingsSingleUpdate($hash, "lastError", "ERR", 1);
return;
}
# ID response: Model, MAC, IP, Protocol, Version, RSSI, DeviceName, SecurityLevel, Generation
if($line =~ /^ID:\s*(.+)/) {
my @parts = split(/,/, $1);
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "model", IntesisWMP_trim($parts[0])) if(defined $parts[0]);
readingsBulkUpdate($hash, "mac", IntesisWMP_trim($parts[1])) if(defined $parts[1]);
readingsBulkUpdate($hash, "ip", IntesisWMP_trim($parts[2])) if(defined $parts[2]);
readingsBulkUpdate($hash, "protocol", IntesisWMP_trim($parts[3])) if(defined $parts[3]);
readingsBulkUpdate($hash, "firmware", IntesisWMP_trim($parts[4])) if(defined $parts[4]);
readingsBulkUpdate($hash, "rssi", IntesisWMP_trim($parts[5])) if(defined $parts[5]);
readingsBulkUpdate($hash, "deviceName", IntesisWMP_trim($parts[6])) if(defined $parts[6]);
readingsBulkUpdate($hash, "securityLevel", IntesisWMP_trim($parts[7])) if(defined $parts[7]);
readingsEndUpdate($hash, 1);
return;
}
# INFO response: Function, Unit, InstallerId, Value
if($line =~ /^INFO:\s*(\w+),\s*(\d+),\s*(\w+),\s*(.+)/) {
my ($func, $unit, $installer, $value) = ($1, $2, $3, IntesisWMP_trim($4));
IntesisWMP_UpdateReading($hash, $func, $value);
return;
}
# CHN (spontaneous change) or GET response: Unit:Function,Value
if($line =~ /^(CHN|GET),\s*(\d+):(\w+),\s*(.+)/) {
my ($cmd, $unit, $func, $value) = ($1, $2, $3, IntesisWMP_trim($4));
Log3 $name, 4, "IntesisWMP ($name): Parsed $cmd response: unit=$unit, func=$func, value=$value";
IntesisWMP_UpdateReading($hash, $func, $value);
return;
}
# Alternative CHN/GET format
if($line =~ /^(\w+),\s*(\d+):(\w+),\s*(.+)/) {
my ($cmd, $unit, $func, $value) = ($1, $2, $3, IntesisWMP_trim($4));
if($cmd eq "CHN" || $cmd eq "GET" || $cmd eq "SET") {
IntesisWMP_UpdateReading($hash, $func, $value);
}
return;
}
# LIMITS response
if($line =~ /^LIMITS:\s*(\w+),\s*\[(.+)\]/) {
my ($func, $values) = ($1, $2);
readingsSingleUpdate($hash, "limits_" . lc($func), $values, 1);
return;
}
# LOGIN response
if($line =~ /^LOGIN:\s*(.+)/) {
my $response = IntesisWMP_trim($1);
if($response eq "OK") {
Log3 $name, 3, "IntesisWMP ($name): Login successful";
readingsSingleUpdate($hash, "login", "ok", 1);
} else {
Log3 $name, 2, "IntesisWMP ($name): Login failed: $response";
readingsSingleUpdate($hash, "login", "failed", 1);
}
return;
}
# DISCOVER response (usually via UDP, but handle anyway)
if($line =~ /^DISCOVER/ || $line =~ /^INWMP/) {
Log3 $name, 4, "IntesisWMP ($name): Discover response: $line";
return;
}
# PING response
if($line eq "PONG") {
Log3 $name, 5, "IntesisWMP ($name): Pong received";
return;
}
Log3 $name, 4, "IntesisWMP ($name): Unhandled message: $line";
}
##############################################
# Update a reading based on function name
##############################################
sub IntesisWMP_UpdateReading($$$) {
my ($hash, $func, $value) = @_;
my $name = $hash->{NAME};
$func = uc($func);
$value = uc($value) unless($value =~ /^-?\d+$/);
Log3 $name, 4, "IntesisWMP ($name): Updating reading for $func = $value";
readingsBeginUpdate($hash);
if($func eq "ONOFF") {
readingsBulkUpdate($hash, "power", lc($value), 1);
# Update state based on power and mode
my $mode = ReadingsVal($name, "mode", "");
if($value eq "ON" && $mode) {
readingsBulkUpdate($hash, "state", lc($mode), 1);
} else {
readingsBulkUpdate($hash, "state", lc($value), 1);
}
}
elsif($func eq "MODE") {
readingsBulkUpdate($hash, "mode", lc($value), 1);
my $power = ReadingsVal($name, "power", "off");
if($power eq "on") {
readingsBulkUpdate($hash, "state", lc($value), 1);
}
}
elsif($func eq "SETPTEMP") {
# Temperature is multiplied by 10
my $temp = $value / 10.0;
readingsBulkUpdate($hash, "desiredTemperature", $temp, 1);
}
elsif($func eq "AMBTEMP") {
# Ambient temperature is multiplied by 10
my $temp = $value / 10.0;
Log3 $name, 3, "IntesisWMP ($name): AMBTEMP received: raw=$value, converted=$temp";
readingsBulkUpdate($hash, "temperature", $temp, 1);
}
elsif($func eq "FANSP") {
readingsBulkUpdate($hash, "fanSpeed", lc($value), 1);
}
elsif($func eq "VANEUD") {
readingsBulkUpdate($hash, "vaneVertical", lc($value), 1);
}
elsif($func eq "VANELR") {
readingsBulkUpdate($hash, "vaneHorizontal", lc($value), 1);
}
elsif($func eq "ERRSTATUS") {
readingsBulkUpdate($hash, "errorStatus", $value, 1);
}
elsif($func eq "ERRCODE") {
readingsBulkUpdate($hash, "errorCode", $value, 1);
}
else {
readingsBulkUpdate($hash, lc($func), $value, 1);
}
readingsEndUpdate($hash, 1);
}
##############################################
# Keepalive timer - sends PING to prevent timeout
##############################################
sub IntesisWMP_Keepalive($) {
my ($hash) = @_;
my $name = $hash->{NAME};
return if(AttrVal($name, "disable", 0));
if($hash->{CONNECTED} && $hash->{CD}) {
Log3 $name, 5, "IntesisWMP ($name): Sending keepalive PING";
IntesisWMP_Send($hash, "PING");
# Schedule next keepalive
IntesisWMP_StartKeepaliveTimer($hash);
} else {
Log3 $name, 4, "IntesisWMP ($name): Keepalive skipped - not connected";
}
return;
}
##############################################
# Update timer - periodically polls all values
##############################################
sub IntesisWMP_UpdateTimer($) {
my ($hash) = @_;
my $name = $hash->{NAME};
return if(AttrVal($name, "disable", 0));
Log3 $name, 3, "IntesisWMP ($name): Update timer triggered, CONNECTED=$hash->{CONNECTED}";
if($hash->{CONNECTED} && $hash->{CD}) {
Log3 $name, 3, "IntesisWMP ($name): Executing periodic update - polling all functions";
# Poll all functions
IntesisWMP_GetAll($hash);
$hash->{LAST_UPDATE} = gettimeofday();
readingsSingleUpdate($hash, ".lastUpdate", scalar(localtime()), 0);
# Schedule next update
IntesisWMP_StartUpdateTimer($hash);
} else {
Log3 $name, 3, "IntesisWMP ($name): Update timer - not connected, skipping poll";
# If not connected, try to reconnect
IntesisWMP_Connect($hash);
}
return;
}
##############################################
# Get all current values from device
# Uses staggered timing to avoid overwhelming the device
##############################################
sub IntesisWMP_GetAll($) {
my ($hash) = @_;
my $name = $hash->{NAME};
my $unit = AttrVal($name, "unit", $hash->{UNIT});
# Get poll functions from attribute or use default
my $pollFuncsAttr = AttrVal($name, "pollFunctions", "");
my @functions;
if($pollFuncsAttr) {
@functions = split(/[,\s]+/, uc($pollFuncsAttr));
} else {
@functions = @{$hash->{helper}{pollFunctions}};
}
Log3 $name, 3, "IntesisWMP ($name): Polling " . scalar(@functions) . " functions with staggered timing";
# Send commands with 0.5 second delays to avoid overwhelming the device
my $delay = 0;
foreach my $func (@functions) {
my $cmd = "GET,$unit:$func";
if($delay == 0) {
# Send first command immediately
IntesisWMP_Send($hash, $cmd);
} else {
# Schedule subsequent commands with delays
InternalTimer(gettimeofday() + $delay, sub {
IntesisWMP_Send($hash, $cmd) if($hash->{CONNECTED});
}, "$name:poll:$func", 0);
}
$delay += 0.5; # 500ms between each command
}
return;
}
##############################################
# Login to device (if security is enabled)
##############################################
sub IntesisWMP_Login($$$) {
my ($hash, $username, $password) = @_;
my $name = $hash->{NAME};
Log3 $name, 4, "IntesisWMP ($name): Logging in...";
IntesisWMP_Send($hash, "LOGIN,$username,$password");
return;
}
##############################################
# Set commands
##############################################
sub IntesisWMP_Set($@) {
my ($hash, @a) = @_;
my $name = shift @a;
my $cmd = shift @a;
my $value = join(" ", @a);
return "\"set $name\" needs at least one argument" if(!defined($cmd));
my $unit = AttrVal($name, "unit", $hash->{UNIT});
# Build list of valid set commands
my $setList = "on:noArg off:noArg " .
"mode:auto,heat,dry,fan,cool " .
"desiredTemperature:slider,16,0.5,32,1 " .
"fanSpeed:auto,1,2,3,4,5,6,7,8,9 " .
"vaneVertical:auto,1,2,3,4,5,swing " .
"vaneHorizontal:auto,1,2,3,4,5,swing " .
"reconnect:noArg " .
"statusRequest:noArg " .
"raw";
if($cmd eq "on") {
return IntesisWMP_Send($hash, "SET,$unit:ONOFF,ON");
}
elsif($cmd eq "off") {
return IntesisWMP_Send($hash, "SET,$unit:ONOFF,OFF");
}
elsif($cmd eq "mode") {
$value = uc($value);
if(!$hash->{helper}{validModes}{$value}) {
return "Invalid mode. Valid modes: auto, heat, dry, fan, cool";
}
return IntesisWMP_Send($hash, "SET,$unit:MODE,$value");
}
elsif($cmd eq "desiredTemperature" || $cmd eq "setptemp") {
# Convert temperature to protocol format (multiply by 10)
if($value !~ /^[\d.]+$/) {
return "Invalid temperature value";
}
my $temp = int($value * 10);
return IntesisWMP_Send($hash, "SET,$unit:SETPTEMP,$temp");
}
elsif($cmd eq "fanSpeed" || $cmd eq "fanspeed") {
$value = uc($value);
if(!$hash->{helper}{validFanSpeeds}{$value}) {
return "Invalid fan speed. Valid values: auto, 1-9";
}
return IntesisWMP_Send($hash, "SET,$unit:FANSP,$value");
}
elsif($cmd eq "vaneVertical" || $cmd eq "vaneud") {
$value = uc($value);
if(!$hash->{helper}{validVanes}{$value}) {
return "Invalid vane position. Valid values: auto, 1-9, swing";
}
return IntesisWMP_Send($hash, "SET,$unit:VANEUD,$value");
}
elsif($cmd eq "vaneHorizontal" || $cmd eq "vanelr") {
$value = uc($value);
if(!$hash->{helper}{validVanes}{$value}) {
return "Invalid vane position. Valid values: auto, 1-9, swing";
}
return IntesisWMP_Send($hash, "SET,$unit:VANELR,$value");
}
elsif($cmd eq "reconnect") {
IntesisWMP_Disconnect($hash);
return IntesisWMP_Connect($hash);
}
elsif($cmd eq "statusRequest") {
Log3 $name, 3, "IntesisWMP ($name): Manual status request triggered";
IntesisWMP_GetAll($hash);
return undef;
}
elsif($cmd eq "raw") {
return IntesisWMP_Send($hash, $value);
}
return "Unknown command $cmd, choose one of $setList";
}
##############################################
# Get commands
##############################################
sub IntesisWMP_Get($@) {
my ($hash, @a) = @_;
my $name = shift @a;
my $cmd = shift @a;
my $value = join(" ", @a);
return "\"get $name\" needs at least one argument" if(!defined($cmd));
my $unit = AttrVal($name, "unit", $hash->{UNIT});
# Build list of valid get commands
my $getList = "update:noArg " .
"deviceInfo:noArg " .
"power:noArg " .
"mode:noArg " .
"desiredTemperature:noArg " .
"temperature:noArg " .
"fanSpeed:noArg " .
"vaneVertical:noArg " .
"vaneHorizontal:noArg " .
"limits:noArg " .
"raw";
if($cmd eq "update") {
Log3 $name, 3, "IntesisWMP ($name): Manual update triggered via get";
IntesisWMP_GetAll($hash);
return undef;
}
elsif($cmd eq "deviceInfo") {
IntesisWMP_Send($hash, "ID");
return undef;
}
elsif($cmd eq "power") {
IntesisWMP_Send($hash, "GET,$unit:ONOFF");
return undef;
}
elsif($cmd eq "mode") {
IntesisWMP_Send($hash, "GET,$unit:MODE");
return undef;
}
elsif($cmd eq "desiredTemperature" || $cmd eq "setptemp") {
IntesisWMP_Send($hash, "GET,$unit:SETPTEMP");
return undef;
}
elsif($cmd eq "temperature" || $cmd eq "ambtemp") {
IntesisWMP_Send($hash, "GET,$unit:AMBTEMP");
return undef;
}
elsif($cmd eq "fanSpeed" || $cmd eq "fanspeed") {
IntesisWMP_Send($hash, "GET,$unit:FANSP");
return undef;
}
elsif($cmd eq "vaneVertical" || $cmd eq "vaneud") {
IntesisWMP_Send($hash, "GET,$unit:VANEUD");
return undef;
}
elsif($cmd eq "vaneHorizontal" || $cmd eq "vanelr") {
IntesisWMP_Send($hash, "GET,$unit:VANELR");
return undef;
}
elsif($cmd eq "limits") {
my @funcs = qw(ONOFF MODE FANSP VANEUD VANELR SETPTEMP);
foreach my $func (@funcs) {
IntesisWMP_Send($hash, "LIMITS:$func");
}
return undef;
}
elsif($cmd eq "raw") {
IntesisWMP_Send($hash, $value);
return undef;
}
return "Unknown command $cmd, choose one of $getList";
}
##############################################
# Attribute handling
##############################################
sub IntesisWMP_Attr(@) {
my ($cmd, $name, $attrName, $attrVal) = @_;
my $hash = $defs{$name};
if($attrName eq "disable") {
if($cmd eq "set" && $attrVal) {
IntesisWMP_Disconnect($hash);
$hash->{STATE} = "disabled";
}
elsif($cmd eq "del" || ($cmd eq "set" && !$attrVal)) {
$hash->{STATE} = "Initialized";
IntesisWMP_Connect($hash) if($init_done);
}
}
elsif($attrName eq "interval") {
if($cmd eq "set") {
$attrVal = 10 if($attrVal < 10);
$attrVal = 55 if($attrVal > 55); # Must be < 60s
$hash->{INTERVAL} = $attrVal;
# Restart timer with new interval
IntesisWMP_StartKeepaliveTimer($hash) if($hash->{CONNECTED});
}
else {
$hash->{INTERVAL} = $WMP_KEEPALIVE_INTERVAL;
}
}
elsif($attrName eq "updateInterval") {
if($cmd eq "set") {
$attrVal = 10 if($attrVal < 10);
$hash->{UPDATE_INTERVAL} = $attrVal;
Log3 $name, 3, "IntesisWMP ($name): Update interval changed to ${attrVal}s";
# Restart update timer with new interval
IntesisWMP_StartUpdateTimer($hash) if($hash->{CONNECTED});
}
else {
$hash->{UPDATE_INTERVAL} = $WMP_UPDATE_INTERVAL;
}
}
elsif($attrName eq "timeout") {
if($cmd eq "set") {
$hash->{TIMEOUT} = $attrVal;
}
else {
$hash->{TIMEOUT} = $WMP_TIMEOUT;
}
}
elsif($attrName eq "unit") {
if($cmd eq "set") {
$hash->{UNIT} = $attrVal;
}
else {
$hash->{UNIT} = 1;
}
}
elsif($attrName eq "pollFunctions") {
if($cmd eq "set" && $attrVal) {
# Validate functions
my @funcs = split(/[,\s]+/, uc($attrVal));
my @validFuncs = qw(ONOFF MODE SETPTEMP AMBTEMP FANSP VANEUD VANELR ERRSTATUS ERRCODE);
my %validHash = map { $_ => 1 } @validFuncs;
foreach my $func (@funcs) {
if(!$validHash{$func}) {
return "Invalid function: $func. Valid functions: " . join(", ", @validFuncs);
}
}
}
}
return undef;
}
##############################################
# Helper: trim whitespace
##############################################
sub IntesisWMP_trim($) {
my ($str) = @_;
return "" if(!defined($str));
$str =~ s/^\s+//;
$str =~ s/\s+$//;
return $str;
}
1;
=pod
=item device
=item summary Control Intesis WMP AC devices (PA-AC-WMP-1)
=item summary_DE Steuerung von Intesis WMP Klimaanlagen (PA-AC-WMP-1)
=begin html
<a name="IntesisWMP"></a>
<h3>IntesisWMP</h3>
<ul>
This module provides control for air conditioning devices via the Intesis WMP
(WiFi Module for Panasonic AC) using the WMP ASCII protocol over TCP.<br><br>
Supported devices include: PA-AC-WMP-1 (INWMPPAN001I000)<br><br>
The module connects to the device on TCP port 3310 and maintains the connection
with periodic keepalive messages. It handles spontaneous state change notifications
from the device (CHN messages) and periodically polls for updates.<br><br>
<a name="IntesisWMP_define"></a>
<b>Define</b>
<ul>
<code>define <name> IntesisWMP <host> [<port>]</code><br><br>
<li><code>host</code>: IP address or hostname of the Intesis device</li>
<li><code>port</code>: TCP port (default: 3310)</li><br>
Example:<br>
<code>define myAC IntesisWMP 192.168.1.100</code><br>
<code>define myAC IntesisWMP ac.local 3310</code>
</ul><br>
<a name="IntesisWMP_set"></a>
<b>Set</b>
<ul>
<li><b>on</b> - Turn AC on</li>
<li><b>off</b> - Turn AC off</li>
<li><b>mode</b> auto|heat|dry|fan|cool - Set operating mode</li>
<li><b>desiredTemperature</b> <temp> - Set target temperature (16-32C)</li>
<li><b>fanSpeed</b> auto|1-9 - Set fan speed</li>
<li><b>vaneVertical</b> auto|1-9|swing - Set vertical vane position</li>
<li><b>vaneHorizontal</b> auto|1-9|swing - Set horizontal vane position</li>
<li><b>statusRequest</b> - Manually trigger an update of all readings</li>
<li><b>reconnect</b> - Reconnect to the device</li>
<li><b>raw</b> <command> - Send raw WMP command</li>
</ul><br>
<a name="IntesisWMP_get"></a>
<b>Get</b>
<ul>
<li><b>update</b> - Refresh all readings from device</li>
<li><b>deviceInfo</b> - Get device identification (ID command)</li>
<li><b>power</b> - Get power state</li>
<li><b>mode</b> - Get current mode</li>
<li><b>desiredTemperature</b> - Get target temperature</li>
<li><b>temperature</b> - Get ambient temperature</li>
<li><b>fanSpeed</b> - Get fan speed</li>
<li><b>vaneVertical</b> - Get vertical vane position</li>
<li><b>vaneHorizontal</b> - Get horizontal vane position</li>
<li><b>limits</b> - Get all function limits</li>
<li><b>raw</b> <command> - Send raw WMP command</li>
</ul><br>
<a name="IntesisWMP_readings"></a>
<b>Readings</b>
<ul>
<li><b>state</b> - Connection state / current mode when on</li>
<li><b>power</b> - Power state (on/off)</li>
<li><b>mode</b> - Operating mode (auto/heat/dry/fan/cool)</li>
<li><b>desiredTemperature</b> - Target temperature in C</li>
<li><b>temperature</b> - Ambient/room temperature in C</li>
<li><b>fanSpeed</b> - Fan speed (auto/1-9)</li>
<li><b>vaneVertical</b> - Vertical vane position</li>
<li><b>vaneHorizontal</b> - Horizontal vane position</li>
<li><b>model</b> - Device model</li>
<li><b>mac</b> - MAC address</li>
<li><b>ip</b> - IP address</li>
<li><b>firmware</b> - Firmware version</li>
<li><b>rssi</b> - WiFi signal strength</li>
</ul><br>
<a name="IntesisWMP_attr"></a>
<b>Attributes</b>
<ul>
<li><b>disable</b> 0|1 - Disable the device</li>
<li><b>interval</b> 10-55 - Keepalive interval in seconds (default: 30)</li>
<li><b>updateInterval</b> - Interval for periodic polling of all values in seconds (default: 60, min: 10)</li>
<li><b>timeout</b> - Connection timeout in seconds (default: 5)</li>
<li><b>unit</b> - Unit number for multi-unit installations (default: 1)</li>
<li><b>username</b> - Username for secured devices</li>
<li><b>password</b> - Password for secured devices</li>
<li><b>tempUnit</b> C|F - Temperature unit for display</li>
<li><b>pollFunctions</b> - Comma-separated list of functions to poll (default: ONOFF,MODE,SETPTEMP,AMBTEMP,FANSP,VANEUD,VANELR)</li>
</ul><br>
<b>RSSI Signal Quality</b><br>
<ul>
<li>> -70: Excellent</li>
<li>-71 to -81: Very Good</li>
<li>-81 to -85: Good</li>
<li>-86 to -95: Weak</li>
<li>< -96: Bad</li>
</ul>
</ul>
=end html
=begin html_DE
<a name="IntesisWMP"></a>
<h3>IntesisWMP</h3>
<ul>
Dieses Modul ermoeglicht die Steuerung von Klimageraeten ueber das Intesis WMP
(WiFi-Modul fuer Panasonic Klimaanlagen) mittels WMP ASCII-Protokoll ueber TCP.<br><br>
Unterstuetzte Geraete: PA-AC-WMP-1 (INWMPPAN001I000)<br><br>
Das Modul verbindet sich ueber TCP-Port 3310 mit dem Geraet und haelt die Verbindung
durch periodische Keepalive-Nachrichten aufrecht. Spontane Statusaenderungen vom
Geraet (CHN-Nachrichten) werden automatisch verarbeitet. Zusaetzlich werden alle
Werte periodisch abgefragt.<br><br>
<a name="IntesisWMP_define"></a>
<b>Define</b>
<ul>
<code>define <name> IntesisWMP <host> [<port>]</code><br><br>
Beispiel:<br>
<code>define meineKlima IntesisWMP 192.168.1.100</code>
</ul><br>
<b>Attribute</b>
<ul>
<li><b>updateInterval</b> - Intervall fuer periodische Abfrage aller Werte in Sekunden (Standard: 60, Min: 10)</li>
<li><b>pollFunctions</b> - Komma-getrennte Liste der abzufragenden Funktionen</li>
</ul>
</ul>
=end html_DE
=cut
my $aaa = $pointB[0]-$pointA[0];
my $bbb = $pointB[1]-$pointA[1];
my $ccc = $pointB[2]-$pointA[2];
$dist = (($aaa)**2)+(($bbb)**2)+(($ccc)**2) ;