change basis code, add little DevIo Helper
This commit is contained in:
parent
b22e96756d
commit
d9c6bb5f24
@ -3,7 +3,8 @@
|
||||
# Copyright notice
|
||||
#
|
||||
# (c) 2016
|
||||
# Copyright: Juergen Kellerer (juergen at k123 dot eu)
|
||||
# Copyright: Juergen Kellerer (juergen at k123 dot eu)
|
||||
# FHEM Maintenance: Marko Oldenburg (leongaultier at gmail dot com)
|
||||
# All rights reserved
|
||||
#
|
||||
# This script free software; you can redistribute it and/or modify
|
||||
@ -38,10 +39,13 @@
|
||||
# - https://github.com/Tristan79/iBrew
|
||||
# .. and to all the volonteers crafting the FHEM project.
|
||||
#
|
||||
# Version: 0.9.1
|
||||
# Version: 1.0.0
|
||||
#
|
||||
#############################################################
|
||||
#
|
||||
# v1.0.0 - 2018-10-19
|
||||
# - change modul code to package module routine
|
||||
# - change code FHEM conform
|
||||
# - add multiple Attribut to control IoDev modul output
|
||||
# v0.9.1 - 2017-04-25
|
||||
# - fixed "stop" detection interferring with "extra" strength.
|
||||
# - added new state "grinding".
|
||||
@ -106,20 +110,86 @@ package main;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $version = "1.0.0";
|
||||
|
||||
sub SmarterCoffee_Initialize($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{DefFn} = 'SmarterCoffee::Define';
|
||||
$hash->{UndefFn} = 'SmarterCoffee::Undefine';
|
||||
$hash->{GetFn} = 'SmarterCoffee::Get';
|
||||
$hash->{SetFn} = 'SmarterCoffee::Set';
|
||||
$hash->{ReadFn} = 'SmarterCoffee::Read';
|
||||
$hash->{ReadyFn} = 'SmarterCoffee::OpenIfRequiredAndWritePending';
|
||||
$hash->{NotifyFn} = 'SmarterCoffee::Notify';
|
||||
$hash->{AttrFn} = 'SmarterCoffee::Attr';
|
||||
|
||||
$hash->{AttrList} = ""
|
||||
."default-hotplate-on-for-minutes "
|
||||
."ignore-max-cups "
|
||||
."set-on-brews-coffee "
|
||||
."strength-coffee-weights "
|
||||
."strength-extra-percent "
|
||||
."strength-extra-pre-brew-cups "
|
||||
."strength-extra-pre-brew-delay-seconds "
|
||||
."strength-extra-start-on-device-strength:off,weak,medium,strong "
|
||||
."devioLoglevel:0,1,2,3,4,5 "
|
||||
.$readingFnAttributes;
|
||||
|
||||
foreach my $d ( sort keys %{ $modules{SmarterCoffee}{defptr} } ) {
|
||||
my $hash = $modules{SmarterCoffee}{defptr}{$d};
|
||||
$hash->{VERSION} = $version;
|
||||
}
|
||||
}
|
||||
|
||||
package SmarterCoffee;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use POSIX;
|
||||
|
||||
use GPUtils qw(:all)
|
||||
; # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt
|
||||
|
||||
use Data::Dumper;
|
||||
use Socket;
|
||||
use IO::Select;
|
||||
|
||||
use DevIo;
|
||||
use HttpUtils;
|
||||
#use HttpUtils;
|
||||
|
||||
my $SmarterCoffee_Port = 2081;
|
||||
my $SmarterCoffee_DiscoveryInterval = 60 * 15;
|
||||
my $SmarterCoffee_StrengthExtraDefaultPercent = 1.4;
|
||||
my $SmarterCoffee_StrengthDefaultWeights = "3.5 3.9 4.3";
|
||||
my %SmarterCoffee_Hotplate = (default => 15, min => 5, max => 40);
|
||||
## Import der FHEM Funktionen
|
||||
BEGIN {
|
||||
GP_Import(
|
||||
qw(readingsSingleUpdate
|
||||
readingsBulkUpdate
|
||||
readingsBeginUpdate
|
||||
readingsEndUpdate
|
||||
CommandAttr
|
||||
defs
|
||||
modules
|
||||
Log3
|
||||
AttrVal
|
||||
ReadingsVal
|
||||
ReadingsNum
|
||||
Value
|
||||
IsDisabled
|
||||
deviceEvents
|
||||
init_done
|
||||
gettimeofday
|
||||
InternalTimer
|
||||
RemoveInternalTimer
|
||||
DoTrigger)
|
||||
);
|
||||
}
|
||||
|
||||
my %SmarterCoffee_MessageMaps = (
|
||||
my $port = 2081;
|
||||
my $discoveryInterval = 60 * 15;
|
||||
my $strengthExtraDefaultPercent = 1.4;
|
||||
my $strengthDefaultWeights = "3.5 3.9 4.3";
|
||||
my %hotplate = (default => 15, min => 5, max => 40);
|
||||
|
||||
my %messageMaps = (
|
||||
status_bitmasks => [
|
||||
# BIT 1 = ???
|
||||
# BIT 2 = hotplate
|
||||
@ -167,7 +237,7 @@ my %SmarterCoffee_MessageMaps = (
|
||||
}
|
||||
);
|
||||
|
||||
my %SmarterCoffee_Commands = (
|
||||
my %commands = (
|
||||
reset => "107e",
|
||||
brew => "377e",
|
||||
brew_with_settings => "33########7e",
|
||||
@ -185,9 +255,9 @@ my %SmarterCoffee_Commands = (
|
||||
history => "467e"
|
||||
);
|
||||
|
||||
my @SmarterCoffee_GetCommands = ("info", "carafe_required_status", "cups_single_mode_status", "get_defaults"); #, "history"
|
||||
my @getCommands = ("info", "carafe_required_status", "cups_single_mode_status", "get_defaults"); #, "history"
|
||||
|
||||
my %SmarterCoffee_ResponseCodes = (
|
||||
my %responseCodes = (
|
||||
'00' => { message => 'Ok', success => 'yes' },
|
||||
'01' => { message => 'Ok, brewing in progress', success => 'yes' },
|
||||
|
||||
@ -200,7 +270,7 @@ my %SmarterCoffee_ResponseCodes = (
|
||||
);
|
||||
|
||||
|
||||
sub SmarterCoffee_ParseMessage {
|
||||
sub ParseMessage {
|
||||
my ($hash, $message) = @_;
|
||||
|
||||
$message = ($hash->{PARTIAL} // "") if (not defined($message));
|
||||
@ -214,7 +284,7 @@ sub SmarterCoffee_ParseMessage {
|
||||
if (int(@messages) > 1) {
|
||||
my $failed;
|
||||
for (@messages) {
|
||||
$failed = 1 if (not SmarterCoffee_ParseMessage($hash, $_."7e"));
|
||||
$failed = 1 if (not ParseMessage($hash, $_."7e"));
|
||||
}
|
||||
return not $failed;
|
||||
}
|
||||
@ -224,16 +294,15 @@ sub SmarterCoffee_ParseMessage {
|
||||
|
||||
# Parse response of a command.
|
||||
if ($message =~ /^03([0-9a-f]{2})7e.*/) {
|
||||
if (my $response = ($SmarterCoffee_ResponseCodes{$1} // 0)) {
|
||||
SmarterCoffee_UpdateReadings($hash,
|
||||
if (my $response = ($responseCodes{$1} // 0)) {
|
||||
UpdateReadings($hash,
|
||||
sub($) {
|
||||
my ($updateReading) = @_;
|
||||
while (my ($key, $value) = each %{$response}) {
|
||||
$updateReading->( "last_command_$key", $value );
|
||||
}
|
||||
$updateReading->( "last_command", $hash->{".last_set_command"} );
|
||||
}, 1
|
||||
, 1);
|
||||
}, 1);
|
||||
} else {
|
||||
Log3 $hash->{NAME}, 3, "Connection :: Unknown command response '$message'.";
|
||||
}
|
||||
@ -243,7 +312,7 @@ sub SmarterCoffee_ParseMessage {
|
||||
if ($message =~ /^47([0-9a-f]{2})(.+)7e.*/) {
|
||||
my @history = split("7d", $2);
|
||||
|
||||
Log 2, Dumper(@history); #TODO
|
||||
Log3 $hash->{NAME}, 5, Dumper(@history); #TODO
|
||||
}
|
||||
|
||||
# Parse default settings message.
|
||||
@ -255,19 +324,19 @@ sub SmarterCoffee_ParseMessage {
|
||||
hotplate => substr($1, 6, 2),
|
||||
);
|
||||
|
||||
SmarterCoffee_ParseStatusValues($hash, \%values);
|
||||
ParseStatusValues($hash, \%values);
|
||||
DoTrigger($hash->{NAME}, "get_defaults");
|
||||
SmarterCoffee_Set($hash, @{[ $hash->{NAME}, "defaults" ]});
|
||||
Set($hash, @{[ $hash->{NAME}, "defaults" ]});
|
||||
}
|
||||
|
||||
# Parse carafe detection status message.
|
||||
if ($message =~ /^4d([0-9a-f]{2})7e.*/) {
|
||||
SmarterCoffee_UpdateReading($hash, "carafe_required", ($1 eq "01" ? "no" : "yes"));
|
||||
UpdateReading($hash, "carafe_required", ($1 eq "01" ? "no" : "yes"));
|
||||
}
|
||||
|
||||
# Parse single cup mode status message.
|
||||
if ($message =~ /^50([0-9a-f]{2})7e.*/) {
|
||||
SmarterCoffee_UpdateReading($hash, "cups_single_mode", ($1 eq "00" ? "no" : "yes"));
|
||||
UpdateReading($hash, "cups_single_mode", ($1 eq "00" ? "no" : "yes"));
|
||||
}
|
||||
|
||||
# Parse info & discovery message.
|
||||
@ -287,7 +356,7 @@ sub SmarterCoffee_ParseMessage {
|
||||
cups => '0'.substr($message, 11, 1),
|
||||
);
|
||||
|
||||
SmarterCoffee_ParseStatusValues($hash, \%values);
|
||||
ParseStatusValues($hash, \%values);
|
||||
}
|
||||
|
||||
$hash->{CONNECTION} = ""
|
||||
@ -301,14 +370,14 @@ sub SmarterCoffee_ParseMessage {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub SmarterCoffee_DumpToExpression($) {
|
||||
sub DumpToExpression($) {
|
||||
my $d = Dumper($_[0]);
|
||||
$d =~ s/\s+/ /g;
|
||||
$d =~ s/[^\}]*(\{.+\})[^\}]*/$1/;
|
||||
return $d;
|
||||
}
|
||||
|
||||
sub SmarterCoffee_ParseStatusValues {
|
||||
sub ParseStatusValues {
|
||||
my ($hash, $values) = @_;
|
||||
|
||||
while (my ($mappingKey, $rawValue) = each %{$values}) {
|
||||
@ -317,20 +386,20 @@ sub SmarterCoffee_ParseStatusValues {
|
||||
my $unpackedStatusBits = sprintf('%08b', ord(pack("H2", $rawValue)));
|
||||
$hash->{".last_status"} .= " ($unpackedStatusBits)";
|
||||
|
||||
for (@{$SmarterCoffee_MessageMaps{"status_bitmasks"}}) {
|
||||
for (@{$messageMaps{"status_bitmasks"}}) {
|
||||
my ($unpackedBitmask, $statusInfo) = @{$_};
|
||||
|
||||
my $bitmask = ord(pack("B8", $unpackedBitmask));
|
||||
if (($bitmask & ord(pack("B8", $unpackedStatusBits))) == $bitmask) {
|
||||
while (my ($k, $v) = each(%{$statusInfo})) { $status{$k} = $v }
|
||||
|
||||
Log3 $hash->{NAME}, 5, "Connection :: Matched all bits of $unpackedBitmask in $unpackedStatusBits. Setting: ".SmarterCoffee_DumpToExpression($statusInfo);
|
||||
Log3 $hash->{NAME}, 5, "Connection :: Matched all bits of $unpackedBitmask in $unpackedStatusBits. Setting: ".DumpToExpression($statusInfo);
|
||||
}
|
||||
}
|
||||
$values->{$mappingKey} = { %status };
|
||||
} else {
|
||||
if (defined($SmarterCoffee_MessageMaps{$mappingKey}{$rawValue})) {
|
||||
$values->{$mappingKey} = $SmarterCoffee_MessageMaps{$mappingKey}{$rawValue};
|
||||
if (defined($messageMaps{$mappingKey}{$rawValue})) {
|
||||
$values->{$mappingKey} = $messageMaps{$mappingKey}{$rawValue};
|
||||
} elsif ($mappingKey eq "hotplate") {
|
||||
$values->{$mappingKey} = { "hotplate_on_for_minutes" => hex($rawValue) };
|
||||
} else {
|
||||
@ -342,7 +411,7 @@ sub SmarterCoffee_ParseStatusValues {
|
||||
|
||||
Log3 $hash->{NAME}, 5, "Connection :: Parsed message: ".Dumper($values);
|
||||
|
||||
SmarterCoffee_UpdateReadings($hash,
|
||||
UpdateReadings($hash,
|
||||
sub($) {
|
||||
my ($updateReading) = @_;
|
||||
my $state = 0;
|
||||
@ -384,7 +453,7 @@ sub SmarterCoffee_ParseStatusValues {
|
||||
);
|
||||
}
|
||||
|
||||
sub SmarterCoffee_Connect($) {
|
||||
sub Connect($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
my $isNewConnection = ReadingsVal($hash->{NAME},'state','none') eq "initializing";
|
||||
@ -393,52 +462,52 @@ sub SmarterCoffee_Connect($) {
|
||||
delete $hash->{INVALID_DEVICE} if defined($hash->{INVALID_DEVICE});
|
||||
|
||||
if ($hash->{AUTO_DETECT}) {
|
||||
SmarterCoffee_RunDiscoveryProcess($hash, 1);
|
||||
RunDiscoveryProcess($hash, 1);
|
||||
}
|
||||
|
||||
if (defined($hash->{DeviceName})) {
|
||||
if (not ($hash->{DeviceName} =~ m/^(.+):([0-9]+)$/)) {
|
||||
$hash->{DeviceName} .= ":$SmarterCoffee_Port";
|
||||
$hash->{DeviceName} .= ":$port";
|
||||
}
|
||||
|
||||
DevIo_CloseDev($hash) if DevIo_IsOpen($hash);
|
||||
main::main::DevIo_CloseDev($hash) if main::DevIo_IsOpen($hash);
|
||||
delete $hash->{DevIoJustClosed} if ($hash->{DevIoJustClosed});
|
||||
|
||||
return SmarterCoffee_OpenIfRequiredAndWritePending($hash, $isNewConnection);
|
||||
return OpenIfRequiredAndWritePending($hash, $isNewConnection);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub SmarterCoffee_OpenIfRequiredAndWritePending($;$) {
|
||||
sub OpenIfRequiredAndWritePending($;$) {
|
||||
my ($hash, $initial) = @_;
|
||||
return DevIo_OpenDev($hash, ($initial ? 0 : 1), "SmarterCoffee_WritePending");
|
||||
return main::DevIo_OpenDev($hash, ($initial ? 0 : 1), "SmarterCoffee::WritePending");
|
||||
}
|
||||
|
||||
sub SmarterCoffee_HandleInitialConnectState($) {
|
||||
sub HandleInitialConnectState($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
return if ($hash->{".initial-connection-state"});
|
||||
|
||||
if (DevIo_IsOpen($hash) and (ReadingsVal($hash->{NAME},'state','none') eq "disconnected" or ReadingsVal($hash->{NAME},'state','none') eq "opened")) {
|
||||
if (main::DevIo_IsOpen($hash) and (ReadingsVal($hash->{NAME},'state','none') eq "disconnected" or ReadingsVal($hash->{NAME},'state','none') eq "opened")) {
|
||||
$hash->{".initial-connection-state"} = 1;
|
||||
|
||||
readingsSingleUpdate($hash,'state','connected',0);
|
||||
SmarterCoffee_Get($hash, @{[ $hash->{NAME}, "info" ]}) if (not $hash->{AUTO_DETECT});
|
||||
SmarterCoffee_Get($hash, @{[ $hash->{NAME}, "carafe_required_status" ]});
|
||||
SmarterCoffee_Get($hash, @{[ $hash->{NAME}, "cups_single_mode_status" ]});
|
||||
Get($hash, @{[ $hash->{NAME}, "info" ]}) if (not $hash->{AUTO_DETECT});
|
||||
Get($hash, @{[ $hash->{NAME}, "carafe_required_status" ]});
|
||||
Get($hash, @{[ $hash->{NAME}, "cups_single_mode_status" ]});
|
||||
|
||||
delete $hash->{".initial-connection-state"};
|
||||
}
|
||||
}
|
||||
|
||||
sub SmarterCoffee_WritePending {
|
||||
sub WritePending {
|
||||
my ($hash, $mustSucceed) = @_;
|
||||
|
||||
if (DevIo_IsOpen($hash)) {
|
||||
if (main::DevIo_IsOpen($hash)) {
|
||||
my $pending = ($hash->{PENDING_COMMAND} // 0);
|
||||
|
||||
# Handling initial call on a fresh connection
|
||||
SmarterCoffee_HandleInitialConnectState($hash);
|
||||
HandleInitialConnectState($hash);
|
||||
|
||||
# Processing pending commands
|
||||
if (($hash->{INVALID_DEVICE} // "0") eq "1") {
|
||||
@ -448,14 +517,14 @@ sub SmarterCoffee_WritePending {
|
||||
delete $hash->{PENDING_COMMAND} if defined($hash->{PENDING_COMMAND});
|
||||
|
||||
Log3 $hash->{NAME}, 4, "Connection :: Sending to ".$hash->{DeviceName}.": $pending";
|
||||
DevIo_SimpleWrite($hash, $pending, 1);
|
||||
main::DevIo_SimpleWrite($hash, $pending, 1);
|
||||
$hash->{".raw_last_status"} = "";
|
||||
|
||||
my $result = DevIo_SimpleReadWithTimeout($hash, 5);
|
||||
my $result = main::DevIo_SimpleReadWithTimeout($hash, 5);
|
||||
if ($result) {
|
||||
$result = SmarterCoffee_Read($hash, $result);
|
||||
$result = Read($hash, $result);
|
||||
} else {
|
||||
DevIo_Disconnected($hash);
|
||||
main::DevIo_Disconnected($hash);
|
||||
}
|
||||
|
||||
$hash->{INVALID_DEVICE} = "1" if ($mustSucceed and not $result);
|
||||
@ -467,11 +536,11 @@ sub SmarterCoffee_WritePending {
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub SmarterCoffee_Read($;$) {
|
||||
sub Read($;$) {
|
||||
my ($hash, $buffer) = @_;
|
||||
|
||||
# Handle case that fhem reconnected a broken connection and state is "opened".
|
||||
SmarterCoffee_HandleInitialConnectState($hash) if (not defined($buffer));
|
||||
HandleInitialConnectState($hash) if (not defined($buffer));
|
||||
|
||||
# Abort read if we already detected that the device is invalid.
|
||||
return 0 if ($hash->{INVALID_DEVICE} // 0);
|
||||
@ -480,7 +549,7 @@ sub SmarterCoffee_Read($;$) {
|
||||
$hash->{PARTIAL} = "" if (not defined($hash->{PARTIAL}) or defined($buffer) or length($hash->{PARTIAL} // "") >= 512);
|
||||
|
||||
# Reading available bytes from the socket (if not specified from external).
|
||||
$buffer = DevIo_SimpleRead($hash) if (not defined($buffer));
|
||||
$buffer = main::DevIo_SimpleRead($hash) if (not defined($buffer));
|
||||
return 0 if (not defined($buffer));
|
||||
|
||||
# Appending message bytes as hex string.
|
||||
@ -488,7 +557,7 @@ sub SmarterCoffee_Read($;$) {
|
||||
|
||||
# Parsing the message and populate readings.
|
||||
if ($hash->{PARTIAL} ne "") {
|
||||
if (SmarterCoffee_ParseMessage($hash)) {
|
||||
if (ParseMessage($hash)) {
|
||||
delete $hash->{PARTIAL};
|
||||
} else {
|
||||
Log3 $hash->{NAME}, 2, "Connection :: Failed parsing buffer content: ".$hash->{PARTIAL};
|
||||
@ -499,48 +568,24 @@ sub SmarterCoffee_Read($;$) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub SmarterCoffee_Initialize($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{DefFn} = 'SmarterCoffee_Define';
|
||||
$hash->{UndefFn} = 'SmarterCoffee_Undefine';
|
||||
$hash->{GetFn} = 'SmarterCoffee_Get';
|
||||
$hash->{SetFn} = 'SmarterCoffee_Set';
|
||||
$hash->{ReadFn} = 'SmarterCoffee_Read';
|
||||
$hash->{ReadyFn} = 'SmarterCoffee_OpenIfRequiredAndWritePending';
|
||||
$hash->{NotifyFn} = 'SmarterCoffee_Notify';
|
||||
|
||||
$hash->{AttrList} = ""
|
||||
."default-hotplate-on-for-minutes "
|
||||
."ignore-max-cups "
|
||||
."set-on-brews-coffee "
|
||||
."strength-coffee-weights "
|
||||
."strength-extra-percent "
|
||||
."strength-extra-pre-brew-cups "
|
||||
."strength-extra-pre-brew-delay-seconds "
|
||||
."strength-extra-start-on-device-strength:off,weak,medium,strong "
|
||||
.$readingFnAttributes;
|
||||
|
||||
Log 5, "Initialized module 'SmarterCoffee'";
|
||||
}
|
||||
|
||||
sub SmarterCoffee_Define($$) {
|
||||
sub Define($$) {
|
||||
my ($hash, $def) = @_;
|
||||
my @param = split('[ \t]+', $def);
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
# set default settings on first define
|
||||
if ($init_done) {
|
||||
$attr{$name}{alias} = "Coffee Machine";
|
||||
$attr{$name}{webCmd} = "strength:cups:start:hotplate:off";
|
||||
$attr{$name}{'strength-extra-percent'} = $SmarterCoffee_StrengthExtraDefaultPercent;
|
||||
$attr{$name}{'default-hotplate-on-for-minutes'} = "15 5=20 8=30 10=35";
|
||||
$attr{$name}{'event-on-change-reading'} = ".*";
|
||||
$attr{$name}{'event-on-update-reading'} = "last_command.*";
|
||||
CommandAttr(undef,$name . ' alias Coffee Machine') if ( AttrVal($name,'alias','none') eq 'none' );
|
||||
CommandAttr(undef,$name . ' webCmd strength:cups:start:hotplate:off') if ( AttrVal($name,'webCmd','none') eq 'none' );
|
||||
CommandAttr(undef,$name . ' strength-extra-percent ' . $strengthExtraDefaultPercent) if ( AttrVal($name,'strength-extra-percent','none') eq 'none' );
|
||||
CommandAttr(undef,$name . ' default-hotplate-on-for-minutes 15 5=20 8=30 10=35') if ( AttrVal($name,'default-hotplate-on-for-minutes','none') eq 'none' );
|
||||
CommandAttr(undef,$name . ' event-on-change-reading .*') if ( AttrVal($name,'event-on-change-reading','none') eq 'none' );
|
||||
CommandAttr(undef,$name . ' event-on-update-reading last_command.*') if ( AttrVal($name,'event-on-update-reading','none') eq 'none' );
|
||||
}
|
||||
|
||||
$attr{$name}{devStateIcon} = '{ SmarterCoffee_GetDevStateIcon($name) }' if not defined($attr{$name}{devStateIcon});
|
||||
CommandAttr(undef,$name . 'devStateIcon { SmarterCoffee::GetDevStateIcon($name) }') if ( AttrVal($name,'devStateIcon','none') eq 'none' or AttrVal($name,'devStateIcon','none') eq '{ SmarterCoffee_GetDevStateIcon($name) }' );
|
||||
|
||||
$hash->{VERSION} = $version;
|
||||
if (int(@param) < 3) {
|
||||
$hash->{AUTO_DETECT} = 1;
|
||||
} else {
|
||||
@ -552,38 +597,62 @@ sub SmarterCoffee_Define($$) {
|
||||
readingsSingleUpdate($hash,'state','initializing',0);
|
||||
|
||||
$hash->{".last_command"} =
|
||||
$hash->{".last_response"} =
|
||||
$hash->{".last_status"} =
|
||||
$hash->{".raw_last_status"} = "";
|
||||
$hash->{".last_response"} =
|
||||
$hash->{".last_status"} =
|
||||
$hash->{".raw_last_status"} = "";
|
||||
|
||||
SmarterCoffee_Connect($hash);
|
||||
Connect($hash);
|
||||
|
||||
$modules{SmarterCoffee}{defptr}{CoolTux} = $hash;
|
||||
|
||||
Log3 $hash->{NAME}, 4, "Instance :: Defined module 'SmarterCoffee': ".Dumper($hash);
|
||||
}
|
||||
|
||||
|
||||
sub SmarterCoffee_Undefine($$) {
|
||||
sub Undefine($$) {
|
||||
my ($hash, $arg) = @_;
|
||||
|
||||
RemoveInternalTimer($hash);
|
||||
DevIo_CloseDev($hash);
|
||||
main::DevIo_CloseDev($hash);
|
||||
|
||||
Log3 $hash->{NAME}, 4, "Instance :: Closed module 'SmarterCoffee': ".Dumper($hash);
|
||||
|
||||
delete( $modules{SmarterCoffee}{defptr}{CoolTux} );
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub SmarterCoffee_Get {
|
||||
sub Attr(@) {
|
||||
|
||||
my ( $cmd, $name, $attrName, $attrVal ) = @_;
|
||||
my $hash = $defs{$name};
|
||||
|
||||
|
||||
if( $attrName eq "devioLoglevel" ) {
|
||||
if( $cmd eq "set" ) {
|
||||
$hash->{devioLoglevel} = $attrVal;
|
||||
Log3 $name, 3, "SmarterCoffee ($name) - set devioLoglevel to $attrVal";
|
||||
|
||||
} elsif( $cmd eq "del" ) {
|
||||
delete $hash->{devioLoglevel};
|
||||
Log3 $name, 3, "SmarterCoffee ($name) - delete Internal devioLoglevel";
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub Get {
|
||||
my ($hash, @param) = @_;
|
||||
|
||||
if (grep {$_ eq ($param[1] // "")} @SmarterCoffee_GetCommands) {
|
||||
return SmarterCoffee_Set($hash, @param) // "Ok :: ".$hash->{".last_response"};
|
||||
if (grep {$_ eq ($param[1] // "")} @getCommands) {
|
||||
return Set($hash, @param) // "Ok :: ".$hash->{".last_response"};
|
||||
} else {
|
||||
return "Unknown argument $param[1], choose one of ".join(":noArg ", @SmarterCoffee_GetCommands).":noArg";
|
||||
return "Unknown argument $param[1], choose one of ".join(":noArg ", @getCommands).":noArg";
|
||||
}
|
||||
}
|
||||
|
||||
sub SmarterCoffee_Set {
|
||||
sub Set {
|
||||
my ($hash, @param) = @_;
|
||||
|
||||
my $desiredCups = defined($hash->{".extra_strength.original_desired_cups"})
|
||||
@ -599,7 +668,7 @@ sub SmarterCoffee_Set {
|
||||
# Special treatment for hotplate, syntax: "set hotplate (on|off) [5-40]"
|
||||
if ($option =~ /^hotplate.*/) {
|
||||
# Select default time from "[minutes] [cups=minutes]", e.g.: "15 5=20 10=35" means: 15 default, 20 from 5 cups and 35 from 10 cups.
|
||||
my ($defaultOnForMinutes, $overrides) = parseParams(AttrVal($hash->{NAME}, "default-hotplate-on-for-minutes", $SmarterCoffee_Hotplate{default}));
|
||||
my ($defaultOnForMinutes, $overrides) = parseParams(AttrVal($hash->{NAME}, "default-hotplate-on-for-minutes", $hotplate{default}));
|
||||
$defaultOnForMinutes = $defaultOnForMinutes->[0] if (defined($defaultOnForMinutes) and int($defaultOnForMinutes) > 0);
|
||||
for my $key (sort { $a <=> $b } (keys %{$overrides})) {
|
||||
$defaultOnForMinutes = $overrides->{$key} if (int($desiredCups) >= int($key));
|
||||
@ -607,17 +676,17 @@ sub SmarterCoffee_Set {
|
||||
|
||||
$value = $optionValue if (not defined($value));
|
||||
$value = $value =~ /^[0-9]+$/ ? int($value) : int($defaultOnForMinutes);
|
||||
$value = $SmarterCoffee_Hotplate{max} if ($value > $SmarterCoffee_Hotplate{max});
|
||||
$value = $SmarterCoffee_Hotplate{min} if ($value < $SmarterCoffee_Hotplate{min});
|
||||
$value = $hotplate{max} if ($value > $hotplate{max});
|
||||
$value = $hotplate{min} if ($value < $hotplate{min});
|
||||
|
||||
SmarterCoffee_UpdateReading($hash, "hotplate_on_for_minutes", ($option eq "hotplate_off" ? 0 : $value));
|
||||
UpdateReading($hash, "hotplate_on_for_minutes", ($option eq "hotplate_off" ? 0 : $value));
|
||||
|
||||
return unpack('H*', pack('C', $value));
|
||||
|
||||
} elsif (defined($SmarterCoffee_MessageMaps{$option}) and defined($optionValue)) {
|
||||
} elsif (defined($messageMaps{$option}) and defined($optionValue)) {
|
||||
# Ordinary values are looked up in the message maps (looking up the HEX code that backs a setting).
|
||||
for my $key (keys %{$SmarterCoffee_MessageMaps{$option}}) {
|
||||
my $v = $SmarterCoffee_MessageMaps{$option}{$key};
|
||||
for my $key (keys %{$messageMaps{$option}}) {
|
||||
my $v = $messageMaps{$option}{$key};
|
||||
if ((ref($v) eq "HASH" ? grep(/^$optionValue$/, values %{$v}) : $v eq $optionValue)) {
|
||||
return $key;
|
||||
}
|
||||
@ -651,13 +720,13 @@ sub SmarterCoffee_Set {
|
||||
# Enable grinder in extra mode if required and option is not defaults.
|
||||
my $grinderEnabled = (($param[3] // ReadingsVal($hash->{NAME}, "grinder", "")) eq "enabled");
|
||||
if ($option ne "defaults" and not $grinderEnabled and ($param[3] // "") ne "disabled") {
|
||||
SmarterCoffee_Set($hash, @{[ $hash->{NAME}, "grinder", "enabled" ]});
|
||||
Set($hash, @{[ $hash->{NAME}, "grinder", "enabled" ]});
|
||||
$grinderEnabled = 1;
|
||||
$param[3] = "enabled" if defined($param[3]);
|
||||
}
|
||||
|
||||
if ($option ne "defaults" and $grinderEnabled) {
|
||||
if (SmarterCoffee_TranslateParamsForExtraStrength($hash, \@param, "grind")) {
|
||||
if (TranslateParamsForExtraStrength($hash, \@param, "grind")) {
|
||||
my ($cups, $error) = ($hash->{".extra_strength.desired_cups"}, $hash->{".extra_strength.error_rate"});
|
||||
Log3 $hash->{NAME}, 3, "Extra Strength :: Grinding [".join(" ", @param)."] to get $cups cups (error rate: $error%).";
|
||||
} else {
|
||||
@ -701,7 +770,7 @@ sub SmarterCoffee_Set {
|
||||
$option = "brew_with_settings";
|
||||
$messagePart = $input{cups}.$input{strength}.$input{hotplate}.$input{grinder};
|
||||
|
||||
SmarterCoffee_UpdateReadings($hash,
|
||||
UpdateReadings($hash,
|
||||
sub($) {
|
||||
my ($updateReading) = @_;
|
||||
for my $key (keys %readingsValues) { $updateReading->( $key, $readingsValues{$key} ) }
|
||||
@ -727,7 +796,7 @@ sub SmarterCoffee_Set {
|
||||
delete $hash->{".extra_strength.enabled"} if ($option eq "strength" and $param[0] ne "extra" and $hash->{".extra_strength.enabled"});
|
||||
|
||||
# Eager updating strength, cups and grinder reading to avoid that widget updates are slower than starting a "brew".
|
||||
SmarterCoffee_UpdateReading($hash, $option, $param[0]) if ($option =~ /^(strength|cups|grinder)$/);
|
||||
UpdateReading($hash, $option, $param[0]) if ($option =~ /^(strength|cups|grinder)$/);
|
||||
|
||||
# Aborting device update when strength is "extra".
|
||||
return undef if ($option eq "strength" and $param[0] eq "extra");
|
||||
@ -738,15 +807,15 @@ sub SmarterCoffee_Set {
|
||||
|
||||
# Resetting internal states before executing "stop".
|
||||
if ($option eq "stop" and ($param[0] // "") ne "no-reset") {
|
||||
SmarterCoffee_ResetState($hash);
|
||||
ResetState($hash);
|
||||
}
|
||||
|
||||
$messagePart = $optionToMessage->( $option, $param[0] );
|
||||
}
|
||||
|
||||
# Command execution
|
||||
if (defined($SmarterCoffee_Commands{$option})) {
|
||||
my $message = $SmarterCoffee_Commands{$option};
|
||||
if (defined($commands{$option})) {
|
||||
my $message = $commands{$option};
|
||||
|
||||
# Replacing placeholders with value.
|
||||
$message =~ s/#+/$messagePart/ if (defined($messagePart) and $messagePart =~ /^[a-f0-9]{2,}$/);
|
||||
@ -758,14 +827,14 @@ sub SmarterCoffee_Set {
|
||||
$hash->{"PENDING_COMMAND"} = $hash->{".last_command"} = $message;
|
||||
Log3 $hash->{NAME}, 4, "Connection :: Sending message: $message [".$hash->{".last_set_command"}."]";
|
||||
|
||||
SmarterCoffee_WritePending($hash, ($option eq "info"));
|
||||
WritePending($hash, ($option eq "info"));
|
||||
}
|
||||
return undef;
|
||||
|
||||
} elsif ($option eq "disconnect" or $option eq "reconnect") {
|
||||
# This option is primarily to test if reconnect works.
|
||||
DevIo_Disconnected($hash);
|
||||
SmarterCoffee_Connect($hash) if ($option eq "reconnect");
|
||||
main::DevIo_Disconnected($hash);
|
||||
Connect($hash) if ($option eq "reconnect");
|
||||
return undef;
|
||||
|
||||
} elsif ($option ne "?" and $option ne "help") {
|
||||
@ -773,7 +842,7 @@ sub SmarterCoffee_Set {
|
||||
}
|
||||
|
||||
my @strength = split(",", "weak,medium,strong,extra");
|
||||
pop(@strength) if (not SmarterCoffee_IsExtraStrengthModeAvailable($hash));
|
||||
pop(@strength) if (not IsExtraStrengthModeAvailable($hash));
|
||||
|
||||
return "Unknown argument $option, choose one of"
|
||||
." brew"
|
||||
@ -787,14 +856,14 @@ sub SmarterCoffee_Set {
|
||||
." hotplate_on_for_minutes:slider,5,5,40";
|
||||
}
|
||||
|
||||
sub SmarterCoffee_ResetState($) {
|
||||
sub ResetState($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
SmarterCoffee_ResetBrewState($hash);
|
||||
SmarterCoffee_ResetExtraStrengthMode($hash);
|
||||
ResetBrewState($hash);
|
||||
ResetExtraStrengthMode($hash);
|
||||
}
|
||||
|
||||
sub SmarterCoffee_Notify($$) {
|
||||
sub Notify($$) {
|
||||
my ($hash, $eventHash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $senderName = $eventHash->{NAME};
|
||||
@ -804,27 +873,27 @@ sub SmarterCoffee_Notify($$) {
|
||||
|
||||
if (my $events = deviceEvents($eventHash, 1)) {
|
||||
if ($senderName eq "global") {
|
||||
SmarterCoffee_ReadConfiguration($hash) if (grep(m/^(INITIALIZED|REREADCFG)$/, @{$events}));
|
||||
ReadConfiguration($hash) if (grep(m/^(INITIALIZED|REREADCFG)$/, @{$events}));
|
||||
} else {
|
||||
for (@{$events}) {
|
||||
if ($_) {
|
||||
SmarterCoffee_ProcessBrewStateEvents($hash, $_);
|
||||
SmarterCoffee_ProcessEventForExtraStrength($hash, $_);
|
||||
SmarterCoffee_LogCommands($hash, $_);
|
||||
ProcessBrewStateEvents($hash, $_);
|
||||
ProcessEventForExtraStrength($hash, $_);
|
||||
LogCommands($hash, $_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub SmarterCoffee_ReadConfiguration($$) {
|
||||
sub ReadConfiguration($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
# Restoring extra strength
|
||||
$hash->{".extra_strength.enabled"} = 1 if (ReadingsVal($hash->{NAME}, "strength", "") =~ /^extra.*/);
|
||||
}
|
||||
|
||||
sub SmarterCoffee_LogCommands($$) {
|
||||
sub LogCommands($$) {
|
||||
my ($hash, $event) = @_;
|
||||
|
||||
if ($event =~ /^last_command_success:\s*(yes|no)\s*$/i and (my $command = ReadingsVal($hash->{NAME}, "last_command", 0))) {
|
||||
@ -837,7 +906,7 @@ sub SmarterCoffee_LogCommands($$) {
|
||||
}
|
||||
}
|
||||
|
||||
sub SmarterCoffee_ProcessBrewStateEvents($$) {
|
||||
sub ProcessBrewStateEvents($$) {
|
||||
my ($hash, $event) = @_;
|
||||
|
||||
# Setting "INITIATED_BREWING" when brewing was initiated by a command (and not by using the machine's buttons)
|
||||
@ -849,26 +918,26 @@ sub SmarterCoffee_ProcessBrewStateEvents($$) {
|
||||
$hash->{".brew-state"} = $1;
|
||||
|
||||
} elsif ($event =~ /^state:\s*done/) {
|
||||
SmarterCoffee_ResetBrewState($hash);
|
||||
ResetBrewState($hash);
|
||||
|
||||
} elsif ($event =~ /^state:\s*(.+)$/ and ($hash->{".brew-state"} // "") =~ /^(brewing|grinding)$/) {
|
||||
Log3 $hash->{NAME}, 3, "Found state change from 'brewing' to '$1'. This looks like an abort, resetting all states to initial.";
|
||||
SmarterCoffee_ResetState($hash);
|
||||
ResetState($hash);
|
||||
}
|
||||
}
|
||||
|
||||
sub SmarterCoffee_ResetBrewState($) {
|
||||
sub ResetBrewState($) {
|
||||
my ($hash) = @_;
|
||||
delete $hash->{".brew-state"} if defined($hash->{".brew-state"});
|
||||
delete $hash->{"INITIATED_BREWING"} if defined($hash->{"INITIATED_BREWING"});
|
||||
}
|
||||
|
||||
sub SmarterCoffee_ProcessEventForExtraStrength($$) {
|
||||
sub ProcessEventForExtraStrength($$) {
|
||||
my ($hash, $event) = @_;
|
||||
|
||||
if ($event =~ /^strength:\s*extra\s*$/) {
|
||||
# Listen to "set strength extra" and enable it if available.
|
||||
if (not (SmarterCoffee_EnableExtraStrengthMode($hash))) {
|
||||
if (not (EnableExtraStrengthMode($hash))) {
|
||||
Log3 $hash->{NAME}, 3, "Extra-Strength :: Downgrading strength 'extra' to 'strong'";
|
||||
fhem("sleep 0.1 fix-strength ; set ".$hash->{NAME}." strength strong");
|
||||
}
|
||||
@ -878,11 +947,11 @@ sub SmarterCoffee_ProcessEventForExtraStrength($$) {
|
||||
if (ReadingsVal($hash->{NAME}, "grinder", "-") eq "disabled"
|
||||
and (my $cups = int(ReadingsVal($hash->{NAME}, "cups", 0))) > 0
|
||||
and (my $strength = ReadingsVal($hash->{NAME}, "strength", "")) eq AttrVal($hash->{NAME}, "strength-extra-start-on-device-strength", "off")
|
||||
and SmarterCoffee_EnableExtraStrengthMode($hash) ) {
|
||||
and EnableExtraStrengthMode($hash) ) {
|
||||
|
||||
Log3 $hash->{NAME}, 3, "Extra-Strength :: Upgrading brewing $cups cups started with disabled grinder and strength '$strength' to strength 'extra'.";
|
||||
SmarterCoffee_Set($hash, @{[ $hash->{NAME}, "stop" ]});
|
||||
SmarterCoffee_Set($hash, @{[ $hash->{NAME}, "brew", $cups, "extra" ]});
|
||||
Set($hash, @{[ $hash->{NAME}, "stop" ]});
|
||||
Set($hash, @{[ $hash->{NAME}, "brew", $cups, "extra" ]});
|
||||
}
|
||||
|
||||
} elsif (($hash->{".extra_strength.enabled"} or $hash->{".extra_strength.phase-2"})) {
|
||||
@ -893,22 +962,22 @@ sub SmarterCoffee_ProcessEventForExtraStrength($$) {
|
||||
} elsif ($event =~ /^state:\s*done/) {
|
||||
# Finishing first round (grinding & first brew are done here)
|
||||
if ((my $delay = int($hash->{".extra_strength.pre_brew_phase_delay"} // 0)) > 0) {
|
||||
InternalTimer(gettimeofday() + $delay, "SmarterCoffee_ExtraStrengthHandleBrewing", $hash, 0);
|
||||
InternalTimer(gettimeofday() + $delay, "SmarterCoffee::ExtraStrengthHandleBrewing", $hash, 0);
|
||||
} else {
|
||||
if (int($hash->{".extra_strength.original_desired_cups"} // 0) > 0) {
|
||||
SmarterCoffee_Set($hash, @{[ $hash->{NAME}, "cups", $hash->{".extra_strength.original_desired_cups"} ]});
|
||||
Set($hash, @{[ $hash->{NAME}, "cups", $hash->{".extra_strength.original_desired_cups"} ]});
|
||||
}
|
||||
SmarterCoffee_ResetExtraStrengthMode($hash);
|
||||
ResetExtraStrengthMode($hash);
|
||||
}
|
||||
|
||||
} elsif ($event =~ /^state:\s*brewing/ and not $hash->{".extra_strength.phase-2"}) {
|
||||
# Entering phase-2: Brewing after initial grinding at different settings.
|
||||
$hash->{".extra_strength.phase-2"} = SmarterCoffee_ExtraStrengthHandleBrewing($hash);
|
||||
$hash->{".extra_strength.phase-2"} = ExtraStrengthHandleBrewing($hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub SmarterCoffee_ExtraStrengthHandleBrewing($) {
|
||||
sub ExtraStrengthHandleBrewing($) {
|
||||
my ($hash) = @_;
|
||||
my @params = (
|
||||
ReadingsVal($hash->{NAME}, "cups", "-"),
|
||||
@ -917,12 +986,12 @@ sub SmarterCoffee_ExtraStrengthHandleBrewing($) {
|
||||
"disabled"
|
||||
);
|
||||
|
||||
if (SmarterCoffee_TranslateParamsForExtraStrength($hash, \@params, "brew")) {
|
||||
if (TranslateParamsForExtraStrength($hash, \@params, "brew")) {
|
||||
# Resetting brew state to ensure it doesn't interfere with stop command that runs with "no-reset" option.
|
||||
SmarterCoffee_ResetBrewState($hash);
|
||||
ResetBrewState($hash);
|
||||
|
||||
# Stopping brewing after initial grinding (skip stop if we are in phase-2 and came here due to pre-brew delay)
|
||||
SmarterCoffee_Set($hash, @{[ $hash->{NAME}, "stop", "no-reset" ]}) if not $hash->{".extra_strength.phase-2"};
|
||||
Set($hash, @{[ $hash->{NAME}, "stop", "no-reset" ]}) if not $hash->{".extra_strength.phase-2"};
|
||||
|
||||
unshift(@params, "brew");
|
||||
unshift(@params, $hash->{NAME});
|
||||
@ -932,17 +1001,17 @@ sub SmarterCoffee_ExtraStrengthHandleBrewing($) {
|
||||
: "2";
|
||||
Log3 $hash->{NAME}, 4, "Extra-Strength :: Phase $phase [set ".join(" ", @params)."]";
|
||||
|
||||
SmarterCoffee_Set($hash, @params);
|
||||
Set($hash, @params);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub SmarterCoffee_IsExtraStrengthModeAvailable($;$) {
|
||||
sub IsExtraStrengthModeAvailable($;$) {
|
||||
my ($hash, $slient) = @_;
|
||||
|
||||
my $extraPercent = AttrVal($hash->{NAME}, "strength-extra-percent", $SmarterCoffee_StrengthExtraDefaultPercent);
|
||||
my $extraPercent = AttrVal($hash->{NAME}, "strength-extra-percent", $strengthExtraDefaultPercent);
|
||||
my $preBrew = int(AttrVal($hash->{NAME}, "strength-extra-pre-brew-cups", 1)) * int(AttrVal($hash->{NAME}, "strength-extra-pre-brew-delay-seconds", 0));
|
||||
|
||||
if ($extraPercent > 0 and ($extraPercent != 1 or $preBrew > 0) and $extraPercent < 2.5) {
|
||||
@ -954,12 +1023,12 @@ sub SmarterCoffee_IsExtraStrengthModeAvailable($;$) {
|
||||
}
|
||||
}
|
||||
|
||||
sub SmarterCoffee_EnableExtraStrengthMode($) {
|
||||
sub EnableExtraStrengthMode($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
return 1 if ($hash->{".extra_strength.enabled"});
|
||||
|
||||
if (SmarterCoffee_IsExtraStrengthModeAvailable($hash, 0)) {
|
||||
if (IsExtraStrengthModeAvailable($hash, 0)) {
|
||||
Log3 $hash->{NAME}, 4, "Extra-Strength :: Entering extra strength mode.";
|
||||
$hash->{".extra_strength.enabled"} = 1;
|
||||
return 1;
|
||||
@ -968,7 +1037,7 @@ sub SmarterCoffee_EnableExtraStrengthMode($) {
|
||||
}
|
||||
}
|
||||
|
||||
sub SmarterCoffee_ResetExtraStrengthMode($;$) {
|
||||
sub ResetExtraStrengthMode($;$) {
|
||||
my ($hash, $partial) = @_;
|
||||
|
||||
Log3 $hash->{NAME}, 4, ("Extra-Strength :: Resetting state to initial (partial: " . ($partial // 0) . ").");
|
||||
@ -986,16 +1055,16 @@ sub SmarterCoffee_ResetExtraStrengthMode($;$) {
|
||||
}
|
||||
}
|
||||
|
||||
sub SmarterCoffee_TranslateParamsForExtraStrength($$$) {
|
||||
sub TranslateParamsForExtraStrength($$$) {
|
||||
my ($hash, $params, $phase) = @_;
|
||||
|
||||
return 0 if (not (SmarterCoffee_EnableExtraStrengthMode($hash)));
|
||||
return 0 if (not (EnableExtraStrengthMode($hash)));
|
||||
|
||||
if ($phase eq "grind") {
|
||||
my $extraPercent = AttrVal($hash->{NAME}, "strength-extra-percent", $SmarterCoffee_StrengthExtraDefaultPercent);
|
||||
my $extraPercent = AttrVal($hash->{NAME}, "strength-extra-percent", $strengthExtraDefaultPercent);
|
||||
|
||||
my @strengths = ("weak", "medium", "strong");
|
||||
my @weights = split(/\s+/, AttrVal($hash->{NAME}, "strength-coffee-weights", $SmarterCoffee_StrengthDefaultWeights));
|
||||
my @weights = split(/\s+/, AttrVal($hash->{NAME}, "strength-coffee-weights", $strengthDefaultWeights));
|
||||
while (int(@weights) < 3) { push(@weights, (int(@weights) ? $weights[int(@weights) - 1] : 4.3)) }
|
||||
|
||||
Log3 $hash->{NAME}, 4, "Extra-Strength :: Reference weights: ".join(" ", @weights)." (".join(" ", @strengths).")";
|
||||
@ -1059,7 +1128,7 @@ sub SmarterCoffee_TranslateParamsForExtraStrength($$$) {
|
||||
$params->[0] = $preBrewCups;
|
||||
} else {
|
||||
$params->[0] = $hash->{".extra_strength.desired_cups"};
|
||||
SmarterCoffee_ResetExtraStrengthMode($hash, 1);
|
||||
ResetExtraStrengthMode($hash, 1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -1068,12 +1137,12 @@ sub SmarterCoffee_TranslateParamsForExtraStrength($$$) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub SmarterCoffee_UpdateReading($$$) {
|
||||
sub UpdateReading($$$) {
|
||||
my ($hash, $name, $value) = @_;
|
||||
SmarterCoffee_UpdateReadings($hash, sub($) { ($_[0])->( $name, $value ) });
|
||||
UpdateReadings($hash, sub($) { ($_[0])->( $name, $value ) });
|
||||
}
|
||||
|
||||
sub SmarterCoffee_UpdateReadings($$;$) {
|
||||
sub UpdateReadings($$;$) {
|
||||
my ($hash, $callback, $forceUpdate) = @_;
|
||||
|
||||
$forceUpdate = (($forceUpdate // 0)
|
||||
@ -1099,29 +1168,29 @@ sub SmarterCoffee_UpdateReadings($$;$) {
|
||||
readingsEndUpdate($hash, ($updated or $forceUpdate));
|
||||
}
|
||||
|
||||
sub SmarterCoffee_RunDiscoveryProcess($;$) {
|
||||
sub RunDiscoveryProcess($;$) {
|
||||
my ($hash, $skipConnect) = @_;
|
||||
|
||||
if (SmarterCoffee_Discover($hash) and not $skipConnect) {
|
||||
SmarterCoffee_Connect($hash);
|
||||
if (Discover($hash) and not $skipConnect) {
|
||||
Connect($hash);
|
||||
}
|
||||
|
||||
InternalTimer(gettimeofday() + $SmarterCoffee_DiscoveryInterval, "SmarterCoffee_RunDiscoveryProcess", $hash, 0);
|
||||
InternalTimer(gettimeofday() + $discoveryInterval, "SmarterCoffee::RunDiscoveryProcess", $hash, 0);
|
||||
}
|
||||
|
||||
sub SmarterCoffee_InetSocketAddressString($) {
|
||||
my ($port, $inetAddress) = sockaddr_in($_[0]);
|
||||
return inet_ntoa($inetAddress).":$port"
|
||||
sub InetSocketAddressString($) {
|
||||
my ($sport, $inetAddress) = sockaddr_in($_[0]);
|
||||
return inet_ntoa($inetAddress).":$sport"
|
||||
}
|
||||
|
||||
sub SmarterCoffee_Discover($) {
|
||||
sub Discover($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
my $existingDeviceName = ($hash->{DeviceName} // "");
|
||||
my $broadcastAddress = sockaddr_in($SmarterCoffee_Port, INADDR_BROADCAST);
|
||||
my $broadcastAddress = sockaddr_in($port, INADDR_BROADCAST);
|
||||
|
||||
Log3 $hash->{NAME}, 4,
|
||||
"Discovery :: Broadcasting discovery request to ".SmarterCoffee_InetSocketAddressString($broadcastAddress)." (already discovered: $existingDeviceName)";
|
||||
"Discovery :: Broadcasting discovery request to ".InetSocketAddressString($broadcastAddress)." (already discovered: $existingDeviceName)";
|
||||
|
||||
socket(my $socket, AF_INET, SOCK_DGRAM, getprotobyname('udp'));
|
||||
setsockopt($socket, SOL_SOCKET, SO_BROADCAST, 1);
|
||||
@ -1130,17 +1199,17 @@ sub SmarterCoffee_Discover($) {
|
||||
|
||||
while ($wait->can_read( 10 )) {
|
||||
my $deviceAddress = recv($socket, my $message, 128, 0);
|
||||
my $inetSocketAddress = SmarterCoffee_InetSocketAddressString($deviceAddress);
|
||||
my $inetSocketAddress = InetSocketAddressString($deviceAddress);
|
||||
|
||||
$message = unpack('H*', $message);
|
||||
|
||||
Log3 $hash->{NAME}, 4, "Discovery :: Received message $message from $inetSocketAddress";
|
||||
|
||||
if ($message =~ /^65.*7e.*/ and SmarterCoffee_ParseMessage($hash, $message)) {
|
||||
my ($port, $inetAddress) = sockaddr_in($deviceAddress);
|
||||
if ($message =~ /^65.*7e.*/ and ParseMessage($hash, $message)) {
|
||||
my ($sport, $inetAddress) = sockaddr_in($deviceAddress);
|
||||
|
||||
if (my ($hostname) = gethostbyaddr($inetAddress, AF_INET)) {
|
||||
$hash->{DeviceName} = $hostname.":$port";
|
||||
$hash->{DeviceName} = $hostname.":$sport";
|
||||
} else {
|
||||
$hash->{DeviceName} = $inetSocketAddress;
|
||||
}
|
||||
@ -1247,7 +1316,7 @@ my $SmarterCoffee_StatusIconSVG = <<XML;
|
||||
</svg>
|
||||
XML
|
||||
|
||||
sub SmarterCoffee_GetDevStateIcon {
|
||||
sub GetDevStateIcon {
|
||||
my ($name, $colors) = @_;
|
||||
|
||||
my ($state, $icon) = (Value($name), $SmarterCoffee_StatusIconSVG);
|
||||
@ -1467,14 +1536,14 @@ sub SmarterCoffee_GetDevStateIcon {
|
||||
<b>Attributes</b><br>
|
||||
<ul>
|
||||
<li>
|
||||
<code>attr <name> devStateIcon { SmarterCoffee_GetDevStateIcon($name) }</code>
|
||||
<code>attr <name> devStateIcon { SmarterCoffee::GetDevStateIcon($name) }</code>
|
||||
<br><br>
|
||||
The function <code>SmarterCoffee_GetDevStateIcon($name[, "...colors..."])</code> renders a custom dev state icon that displays
|
||||
The function <code>SmarterCoffee::GetDevStateIcon($name[, "...colors..."])</code> renders a custom dev state icon that displays
|
||||
the machine states (ready, brewing, done) and shows information on carafe, hotplate and water level.
|
||||
<br><br>
|
||||
The icon is monochrome using a default color that may change to highlight states: ready, brewing, done.
|
||||
Built-in colors can be adjusted with the second parameter of <code>SmarterCoffee_GetDevStateIcon</code>.<br>
|
||||
E.g. using "<code>attr <name> devStateIcon { SmarterCoffee_GetDevStateIcon($name, '#7b7b7b green chocolate #336699' }</code>"
|
||||
Built-in colors can be adjusted with the second parameter of <code>SmarterCoffee::GetDevStateIcon</code>.<br>
|
||||
E.g. using "<code>attr <name> devStateIcon { SmarterCoffee::GetDevStateIcon($name, '#7b7b7b green chocolate #336699' }</code>"
|
||||
sets colors for default, ready, brewing and done.
|
||||
<br><br>
|
||||
Colors are specified as HTML color values delimited by whitespace using a fixed order of "default ready brewing done".
|
||||
|
Loading…
x
Reference in New Issue
Block a user