mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-28 11:01:59 +00:00
99_Venetian: removed /FHEM/lib/VenetianBlinds as it's against the conventions
git-svn-id: https://svn.fhem.de/fhem/trunk@12249 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
01d49ad8c1
commit
1d8eafdca1
@ -1,89 +0,0 @@
|
|||||||
##############################################
|
|
||||||
#
|
|
||||||
# This is open source software licensed unter the Apache License 2.0
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
##############################################
|
|
||||||
|
|
||||||
package VenetianBlinds::Shared;
|
|
||||||
use v5.14;
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use experimental "smartmatch";
|
|
||||||
use base 'Exporter';
|
|
||||||
|
|
||||||
|
|
||||||
# constants #############################################
|
|
||||||
use constant scenes => {
|
|
||||||
"open" => {
|
|
||||||
"blind" => 99,
|
|
||||||
"slat" => 99,
|
|
||||||
},
|
|
||||||
"closed" => {
|
|
||||||
"blind" => 0,
|
|
||||||
"slat" => 0,
|
|
||||||
},
|
|
||||||
"see_through" => {
|
|
||||||
"blind" => 0,
|
|
||||||
"slat" => 50,
|
|
||||||
},
|
|
||||||
"shaded" => {
|
|
||||||
"blind" => 0,
|
|
||||||
"slat" => 30,
|
|
||||||
},
|
|
||||||
"adaptive" => {
|
|
||||||
"blind" => 0,
|
|
||||||
"slat" => "adaptive",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
our @EXPORT_OK = ('scenes');
|
|
||||||
|
|
||||||
# functions #############################################
|
|
||||||
|
|
||||||
sub send_to_all{
|
|
||||||
my ($cmd) = @_;
|
|
||||||
foreach my $device (find_devices()) {
|
|
||||||
main::fhem("set $device $cmd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub find_devices_in_room {
|
|
||||||
my ($my_room) = @_;
|
|
||||||
my @result = ();
|
|
||||||
my @devices = find_devices();
|
|
||||||
foreach my $device (@devices){
|
|
||||||
my $rooms = main::AttrVal($device,"room",undef);
|
|
||||||
if (defined $rooms){
|
|
||||||
foreach my $room (split(/,/, $rooms)){
|
|
||||||
if ($my_room eq $room){
|
|
||||||
push(@result,$device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
main::Log(3,"Blinds '$device' not mapped to a room");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return @result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sub find_devices{
|
|
||||||
my $devstr = main::fhem("list .* type");
|
|
||||||
my @result = ();
|
|
||||||
foreach my $device (split /\n/, $devstr) {
|
|
||||||
$device =~ s/^\s+|\s+$//g; # trim white spaces
|
|
||||||
if( length($device) > 0){
|
|
||||||
$device =~ /^(\S+)\s+(\S+)$/;
|
|
||||||
my $devname = $1;
|
|
||||||
my $model = $2;
|
|
||||||
if ($model eq "VenetianBlindController"){
|
|
||||||
push(@result,$devname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return @result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
1;
|
|
@ -1,283 +0,0 @@
|
|||||||
##############################################
|
|
||||||
#
|
|
||||||
# This is open source software licensed unter the Apache License 2.0
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
##############################################
|
|
||||||
package VenetianBlinds::VenetianBlindController;
|
|
||||||
|
|
||||||
use v5.10.1;
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use experimental "smartmatch";
|
|
||||||
use VenetianBlinds::Shared "scenes";
|
|
||||||
|
|
||||||
|
|
||||||
# constants ########################
|
|
||||||
|
|
||||||
use constant blind_threshold => 5; #percentage points
|
|
||||||
use constant slat_threshold => 5; #percentage points
|
|
||||||
use constant power_threshold => 10; #watts
|
|
||||||
|
|
||||||
# FHEM commands ########################
|
|
||||||
|
|
||||||
sub Define{
|
|
||||||
#TODO: check if $device and $master really exist
|
|
||||||
my ($hash,$a,$h) = @_;
|
|
||||||
|
|
||||||
if (!defined $h->{master}) {
|
|
||||||
return "Mandatory argument 'master=<name>' is missing or undefined";
|
|
||||||
}
|
|
||||||
$hash->{master_controller} = $h->{master};
|
|
||||||
|
|
||||||
if (!defined $h->{device}) {
|
|
||||||
return "Mandatory argument 'device=<name>' is missing or undefined";
|
|
||||||
}
|
|
||||||
$hash->{device} = $h->{device};
|
|
||||||
|
|
||||||
if (!defined $h->{could_index_threshold}) {
|
|
||||||
return "Mandatory argument 'could_index_threshold=<value>' is missing or undefined";
|
|
||||||
}
|
|
||||||
$hash->{could_index_threshold} = $h->{could_index_threshold};
|
|
||||||
|
|
||||||
if (!defined $h->{azimuth} ) {
|
|
||||||
return "Mandatory argument 'azimuth=<start>-<end>' is missing or undefined";
|
|
||||||
}
|
|
||||||
my ($azstart,$azend) = split(/-/, $h->{azimuth});
|
|
||||||
$hash->{azimuth_start} = $azstart;
|
|
||||||
$hash->{azimuth_end} = $azend;
|
|
||||||
|
|
||||||
if (!defined $h->{elevation}) {
|
|
||||||
return "Mandatory argument 'elevation=<start>-<end>' is missing or undefined";
|
|
||||||
}
|
|
||||||
my ($evstart,$evend) = split(/-/, $h->{elevation});
|
|
||||||
$hash->{elevation_start} = $evstart;
|
|
||||||
$hash->{elevation_end} = $evend;
|
|
||||||
|
|
||||||
if (!defined $h->{months} ) {
|
|
||||||
return "Mandatory argument 'months=<start>-<end>' is missing or undefined";
|
|
||||||
} $hash->{azimuth_start} = $azstart;
|
|
||||||
my ($monstart,$monend) = split(/-/, $h->{months});
|
|
||||||
$hash->{month_start} = $monstart;
|
|
||||||
$hash->{month_end} = $monend;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub Set{
|
|
||||||
my ( $hash, $a,$h ) = @_;
|
|
||||||
my $cmd = $a->[1];
|
|
||||||
my @scene_list = keys %{&scenes};
|
|
||||||
if ( $cmd eq "?" ){
|
|
||||||
my $result = "automatic:noArg wind_alarm:noArg stop:noArg";
|
|
||||||
foreach my $scene (@scene_list){
|
|
||||||
$result .= " $scene:noArg";
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
} elsif ($cmd eq "automatic") {
|
|
||||||
main::readingsSingleUpdate($hash,"automatic",1,1);
|
|
||||||
update_automatic($hash,1);
|
|
||||||
} elsif ($cmd ~~ @scene_list) {
|
|
||||||
main::readingsSingleUpdate($hash,"automatic",0,1);
|
|
||||||
set_scene($hash, $cmd, 0);
|
|
||||||
} elsif ($cmd eq "scenes") {
|
|
||||||
delete $hash->{scences};
|
|
||||||
} elsif ($cmd eq "wind_alarm") {
|
|
||||||
wind_alarm($hash);
|
|
||||||
} elsif ($cmd eq "stop") {
|
|
||||||
stop($hash);
|
|
||||||
} else {
|
|
||||||
return "unknown command $cmd";
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sub Notify{
|
|
||||||
my ($hash, $devName, $events) = @_;
|
|
||||||
if ($devName eq $hash->{master_controller}){
|
|
||||||
update_automatic($hash,0);
|
|
||||||
} elsif ($devName eq $hash->{device}) {
|
|
||||||
update_STATE($hash);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# logic for blind control #####################
|
|
||||||
sub update_automatic{
|
|
||||||
my ($hash,$force) = @_;
|
|
||||||
my $master = $hash->{master_controller};
|
|
||||||
my $sun_elevation = main::ReadingsVal($master, "sun_elevation", undef);
|
|
||||||
my $sun_azimuth = main::ReadingsVal($master, "sun_azimuth", undef);
|
|
||||||
my $wind_speed = main::ReadingsVal($master, "wind_speed", undef);
|
|
||||||
my $wind_alarm = main::ReadingsVal($master, "wind_alarm", undef);
|
|
||||||
my $cloud_index = main::ReadingsVal($master, "cloud_index", undef);
|
|
||||||
my $month = main::ReadingsVal($master, "month", undef);
|
|
||||||
my $automatic = main::ReadingsVal($hash->{NAME}, "automatic", undef);
|
|
||||||
my $old_scene = main::ReadingsVal($hash->{NAME}, "scene", undef);
|
|
||||||
my $mechanical_switches = main::ReadingsVal($hash->{device}, "reportedState", undef);
|
|
||||||
|
|
||||||
# reasons to not work in automatic mode
|
|
||||||
if ($wind_alarm
|
|
||||||
or !$automatic
|
|
||||||
or $month < $hash->{month_start}
|
|
||||||
or $month > $hash->{month_end}
|
|
||||||
or $mechanical_switches eq "setOn"
|
|
||||||
or $mechanical_switches eq "setOff" ){
|
|
||||||
return;
|
|
||||||
main::Log(3,"Automatic inactive on $hash->{NAME}");
|
|
||||||
}
|
|
||||||
|
|
||||||
my $new_scene = undef;
|
|
||||||
if ($hash->{elevation_start} <= $sun_elevation and
|
|
||||||
$sun_elevation <= $hash->{elevation_end} and
|
|
||||||
$hash->{azimuth_start} <= $sun_azimuth and
|
|
||||||
$sun_azimuth <= $hash->{azimuth_end} and
|
|
||||||
$cloud_index <= $hash->{could_index_threshold}) {
|
|
||||||
$new_scene ="shaded";
|
|
||||||
} else {
|
|
||||||
$new_scene = "open";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($force or !($new_scene eq $old_scene) or ($new_scene eq "adaptive") ) {
|
|
||||||
set_scene($hash,$new_scene,0);
|
|
||||||
} else {
|
|
||||||
main::Log(5,"Scene has not changed on $hash->{NAME}, not moving blinds");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# smart slat control #######################
|
|
||||||
#
|
|
||||||
# This equation converts the elevation of the sun to the angle of the slats.
|
|
||||||
# It's an approximation of the trigonometric functions for my slat geometry.
|
|
||||||
#
|
|
||||||
# The goal of the "adaptive" mode is to set the angle of the slats just closed
|
|
||||||
# enough that the sun can't get in. So if the sun elevation is very high
|
|
||||||
# (e.g. 60°) the slats should be vertical so that the sun can't get in, but you
|
|
||||||
# can still look out. If the elevation of the sun is very low (e.g. 10°) the
|
|
||||||
# slats should be fully closed as the sun would otherwise get through the slats.
|
|
||||||
#
|
|
||||||
# So the task is to compute the angle of the slats in a way that the sun does
|
|
||||||
# not get in, but you still can look through.
|
|
||||||
#
|
|
||||||
# For that we need to know the geometry of the slats. For my slats: a=7.2cm
|
|
||||||
# (distance between two slats) and b=8cm (length of the slats).
|
|
||||||
# the geometry is explained here: doc/adaptive_mode/slat_geomertry.jpg
|
|
||||||
#
|
|
||||||
# As it's quite compicated to solve the equation for beta, I created a spread
|
|
||||||
# sheet and used that to get an approximation for the curves. And this
|
|
||||||
# approximation is then implemented in this function. The spread sheet is in
|
|
||||||
# doc/adaptive_mode/approximation_for_slat_geomertry.ods
|
|
||||||
#
|
|
||||||
# TODO: to implement this properly, we need to make the slat geometry configurable
|
|
||||||
|
|
||||||
|
|
||||||
sub get_slats_for_elevation{
|
|
||||||
my ($hash) = @_;
|
|
||||||
my $master = $hash->{master_controller};
|
|
||||||
my $elevation = main::ReadingsVal($master, "sun_elevation", undef);
|
|
||||||
if ($elevation >= 45) {
|
|
||||||
return 50;
|
|
||||||
} elsif ($elevation <= 10) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return int($elevation*1.16 + 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# move the blinds ##########################
|
|
||||||
sub set_scene{
|
|
||||||
my ($hash,$scene,$force) = @_;
|
|
||||||
my $automatic = main::ReadingsVal($hash->{NAME}, "automatic", undef);
|
|
||||||
my $old_scene = main::ReadingsVal($hash->{NAME}, "scene", undef);
|
|
||||||
|
|
||||||
if (!defined &scenes->{$scene}){
|
|
||||||
main::Log(1, "undefined scene &scenes->{$scene}");
|
|
||||||
} else {
|
|
||||||
main::readingsSingleUpdate($hash,"scene",$scene,1);
|
|
||||||
main::Log(3,"moving blinds $hash->{device} to scene $scene.");
|
|
||||||
move_blinds($hash, &scenes->{$scene}{blind}, &scenes->{$scene}{slat});
|
|
||||||
}
|
|
||||||
update_STATE($hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub update_STATE {
|
|
||||||
my ($hash) = @_;
|
|
||||||
my $automatic = main::ReadingsVal($hash->{NAME}, "automatic", undef);
|
|
||||||
my $scene = main::ReadingsVal($hash->{NAME}, "scene", undef);
|
|
||||||
my $mechanical_switches = main::ReadingsVal($hash->{device}, "reportedState", undef);
|
|
||||||
|
|
||||||
if ($mechanical_switches eq "setOn") {
|
|
||||||
$hash->{STATE} = "mechanical: Up";
|
|
||||||
} elsif ( $mechanical_switches eq "setOff") {
|
|
||||||
$hash->{STATE} = "mechanical: Down";
|
|
||||||
} elsif ($automatic) {
|
|
||||||
$hash->{STATE} = "automatic: $scene";
|
|
||||||
} else {
|
|
||||||
$hash->{STATE} = "manual: $scene";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub move_blinds{
|
|
||||||
my ($hash, $blind, $slat)= @_;
|
|
||||||
my ($current_blind, $current_slat) = get_position($hash);
|
|
||||||
if (defined $slat and $slat eq "adaptive") {
|
|
||||||
$slat = get_slats_for_elevation($hash);
|
|
||||||
}
|
|
||||||
if ( defined $blind and
|
|
||||||
abs($blind-$current_blind) > &blind_threshold ){
|
|
||||||
main::fhem("set $hash->{device} positionBlinds $blind");
|
|
||||||
count_commands($hash);
|
|
||||||
}
|
|
||||||
if ( defined $slat and
|
|
||||||
abs($slat - $current_slat) > &slat_threshold ){
|
|
||||||
main::fhem("set $hash->{device} positionSlat $slat");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub wind_alarm{
|
|
||||||
my ($hash) = @_;
|
|
||||||
move_blinds($hash,99,undef);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub stop {
|
|
||||||
my ($hash) = @_;
|
|
||||||
main::fhem("set $hash->{device} stop");
|
|
||||||
count_commands($hash);
|
|
||||||
delete $hash->{queue};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub count_commands{
|
|
||||||
my ($hash) = @_;
|
|
||||||
my $count = main::ReadingsVal($hash->{NAME}, "command_count", 0) +1;
|
|
||||||
main::readingsSingleUpdate($hash,"command_count",$count,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# wrappers around readings #############################
|
|
||||||
sub get_power{
|
|
||||||
my ($hash) = @_;
|
|
||||||
main::fhem("get $hash->{device} smStatus", undef);
|
|
||||||
my $power_reading = main::ReadingsVal($hash->{device}, "power", undef);
|
|
||||||
$power_reading =~ /([\d\.]+)\WW/;
|
|
||||||
if (!defined $1){
|
|
||||||
main::Log(1,"Error reading power level of $hash->{device}:'$power_reading'");
|
|
||||||
}
|
|
||||||
return $1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_position{
|
|
||||||
my ($hash) = @_;
|
|
||||||
main::fhem("get $hash->{device} position");
|
|
||||||
my $device=$hash->{device};
|
|
||||||
#TODO: do we really need a ReadingsVal or does the "get position" also deliver that result?
|
|
||||||
my $position = main::ReadingsVal($device, "position", undef);
|
|
||||||
$position =~ /Blind (\d+) Slat (\d+)/;
|
|
||||||
if (!defined $1 or !defined $2){
|
|
||||||
main::Log( 1, "Error: could not get position of device $hash->{device}: $position");
|
|
||||||
}
|
|
||||||
return ($1,$2);
|
|
||||||
}
|
|
||||||
|
|
||||||
1; # end module
|
|
@ -1,185 +0,0 @@
|
|||||||
##############################################
|
|
||||||
#
|
|
||||||
# This is open source software licensed unter the Apache License 2.0
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
##############################################
|
|
||||||
|
|
||||||
package VenetianBlinds::VenetianMasterController;
|
|
||||||
use v5.14;
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use experimental "smartmatch";
|
|
||||||
use VenetianBlinds::Shared;
|
|
||||||
|
|
||||||
# Map the condition codes from yahoo to cloudiness index,
|
|
||||||
# makes it easier to implement thresholds as higher number indicates more clouds
|
|
||||||
# https://developer.yahoo.com/weather/documentation.html
|
|
||||||
# https://de.wikipedia.org/wiki/Bew%C3%B6lkung#Einteilung_des_Flugwetterdienstes
|
|
||||||
|
|
||||||
my $yahoo_code_map = {
|
|
||||||
#TODO: add mapping for more codes
|
|
||||||
23 => 4, # blustery
|
|
||||||
24 => 4, # windy
|
|
||||||
26 => 5, # cloudy
|
|
||||||
28 => 6, # mostly cloudy (day)
|
|
||||||
30 => 3, # partly cloudy (day)
|
|
||||||
32 => 1, # sunny
|
|
||||||
34 => 2, # fair (day)
|
|
||||||
36 => 0, # hot
|
|
||||||
38 => 5, #scattered thunderstorms
|
|
||||||
39 => 5, #scattered thunderstorms
|
|
||||||
};
|
|
||||||
|
|
||||||
sub Define{
|
|
||||||
my ($hash,$a,$h) = @_;
|
|
||||||
|
|
||||||
if (!defined $h->{twilight}) {
|
|
||||||
return "Mandatory argument 'twilight=<name>' is missing or undefined";
|
|
||||||
}
|
|
||||||
$hash->{twilight} = $h->{twilight};
|
|
||||||
|
|
||||||
if (!defined $h->{weather}) {
|
|
||||||
return "Mandatory argument 'weather=<name>' is missing or undefined";
|
|
||||||
}
|
|
||||||
$hash->{weather} = $h->{weather};
|
|
||||||
|
|
||||||
if (!defined $h->{wind_speed_threshold}) {
|
|
||||||
return "Mandatory argument 'wind_speed_threshold=<value>' is missing or undefined";
|
|
||||||
}
|
|
||||||
$hash->{wind_speed_threshold} = $h->{wind_speed_threshold};
|
|
||||||
|
|
||||||
$hash->{STATE} = "OK";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub Set{
|
|
||||||
my ($hash,$a,$h) = @_;
|
|
||||||
my $cmd = $a->[1];
|
|
||||||
if ($cmd eq "?"){
|
|
||||||
return "trigger_update:noArg stop:noArg automatic:noArg";
|
|
||||||
} elsif ($cmd eq "trigger_update") {
|
|
||||||
trigger_update($hash);
|
|
||||||
} elsif ($cmd eq "stop") {
|
|
||||||
VenetianBlinds::Shared::send_to_all("stop");
|
|
||||||
} elsif ($cmd eq "automatic") {
|
|
||||||
VenetianBlinds::Shared::send_to_all("automatic");
|
|
||||||
} else {
|
|
||||||
return "unknown command $cmd";
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sub Notify{
|
|
||||||
my ($hash, $devName, $events) = @_;
|
|
||||||
if ($devName eq $hash->{twilight}) {
|
|
||||||
update_twilight($hash);
|
|
||||||
} elsif ($devName eq $hash->{weather}){
|
|
||||||
update_weather($hash);
|
|
||||||
check_wind_alarm($hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
#foreach my $event (@{$events}) {
|
|
||||||
# $event = "" if(!defined($event));
|
|
||||||
# main::Log(3,"Event on device $devName: $event");
|
|
||||||
#}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub trigger_update {
|
|
||||||
my ($hash) = @_;
|
|
||||||
update_twilight($hash);
|
|
||||||
update_calendar($hash);
|
|
||||||
update_weather($hash);
|
|
||||||
check_wind_alarm($hash);
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
sub update_twilight{
|
|
||||||
my ($hash) = @_;
|
|
||||||
# TODO: reduce number of events: only trigger event if data has changed
|
|
||||||
main::readingsBeginUpdate($hash);
|
|
||||||
main::readingsBulkUpdate($hash, "sun_elevation",
|
|
||||||
main::ReadingsVal($hash->{twilight}, "elevation", undef) );
|
|
||||||
main::readingsBulkUpdate($hash, "sun_azimuth",
|
|
||||||
main::ReadingsVal($hash->{twilight}, "azimuth", undef) );
|
|
||||||
main::readingsEndUpdate($hash, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub update_calendar{
|
|
||||||
my ($hash) = @_;
|
|
||||||
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
|
|
||||||
# Note: months start at 0 = January
|
|
||||||
$mon +=1;
|
|
||||||
my $current = main::ReadingsVal($hash->{NAME}, "month", undef);
|
|
||||||
if ($mon != $current){
|
|
||||||
main::readingsSingleUpdate($hash,"month",$mon,1);
|
|
||||||
}
|
|
||||||
#TODO: do the update exactly at midnight
|
|
||||||
main::InternalTimer(main::gettimeofday()+24*60*60, "VenetianMasterController::update_calendar", $hash, 1);
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub update_weather{
|
|
||||||
my ($hash) = @_;
|
|
||||||
my $condition_code = main::ReadingsVal($hash->{weather}, "code", undef);
|
|
||||||
if (!defined $condition_code) {
|
|
||||||
main::Log(1,"could not get Weather condition code from '$hash->{weather}'");
|
|
||||||
}
|
|
||||||
my $cloud_index = undef;
|
|
||||||
$cloud_index = $yahoo_code_map->{$condition_code};
|
|
||||||
if (!defined $cloud_index){
|
|
||||||
$cloud_index = 9;
|
|
||||||
};
|
|
||||||
my $wind_speed = main::ReadingsVal($hash->{weather}, "wind_speed", undef);
|
|
||||||
if (!defined $wind_speed) {
|
|
||||||
main::Log(1,"could not get Weather wind_speed from '$hash->{weather}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
# TODO: reduce number of events: only trigger event if data has changed
|
|
||||||
main::readingsBeginUpdate($hash);
|
|
||||||
main::readingsBulkUpdate($hash, "wind_speed", $wind_speed);
|
|
||||||
main::readingsBulkUpdate($hash, "cloud_index", $cloud_index);
|
|
||||||
main::readingsEndUpdate($hash, 1);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub check_wind_alarm{
|
|
||||||
my ($hash) = @_;
|
|
||||||
my $windspeed = main::ReadingsVal($hash->{NAME}, "wind_speed", undef);
|
|
||||||
my $windalarm = main::ReadingsVal($hash->{NAME}, "wind_alarm", undef);
|
|
||||||
given ($windalarm) {
|
|
||||||
when (0) {
|
|
||||||
if (($windspeed >= $hash->{wind_speed_threshold})){
|
|
||||||
main::Log(3,"Wind alarm: $windspeed km/h");
|
|
||||||
main::readingsSingleUpdate($hash,"wind_alarm",1,1);
|
|
||||||
VenetianBlinds::Shared::send_to_all("wind_alarm");
|
|
||||||
$hash->{STATE} = "wind alarm";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when (1) {
|
|
||||||
if (($windspeed >= $hash->{wind_speed_threshold})){
|
|
||||||
main::readingsSingleUpdate($hash,"wind_alarm",1,1);
|
|
||||||
} else {
|
|
||||||
if (main::ReadingsAge($hash->{NAME},"wind_speed",undef) > 600) {
|
|
||||||
main::readingsSingleUpdate($hash,"wind_alarm",0,1);
|
|
||||||
main::Log(3,"Wind alarm ended.");
|
|
||||||
$hash->{STATE} = "normal";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
when (undef) {
|
|
||||||
main::readingsSingleUpdate($hash,"wind_alarm",0,0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
1;
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
|||||||
##############################################
|
|
||||||
#
|
|
||||||
# This is open source software licensed unter the Apache License 2.0
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
##############################################
|
|
||||||
|
|
||||||
package VenetianBlinds::VenetianRoomController;
|
|
||||||
|
|
||||||
use v5.10.1;
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use experimental "smartmatch";
|
|
||||||
use VenetianBlinds::Shared;
|
|
||||||
|
|
||||||
|
|
||||||
# fhem interface #############################################
|
|
||||||
sub Define{
|
|
||||||
my ($hash,$a,$h) = @_;
|
|
||||||
$hash->{STATE} = "OK";
|
|
||||||
if (defined $h->{rooms}) {
|
|
||||||
$hash->{rooms} = $h->{rooms};
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub Notify {
|
|
||||||
my ($hash, $devName, $events) = @_;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub Set{
|
|
||||||
my ($hash,$a,$h) = @_;
|
|
||||||
my $cmd = $a->[1];
|
|
||||||
my @scene_list = keys %{&VenetianBlinds::Shared::scenes};
|
|
||||||
given ($cmd) {
|
|
||||||
when ("?") {
|
|
||||||
my $result = "automatic:noArg stop:noArg automatic:noArg";
|
|
||||||
foreach my $scene (@scene_list){
|
|
||||||
$result .= " $scene:noArg";
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
when ("automatic") {
|
|
||||||
send_to_all_in_my_rooms($hash, "automatic");
|
|
||||||
}
|
|
||||||
when ("stop"){
|
|
||||||
send_to_all_in_my_rooms($hash, "stop");
|
|
||||||
}
|
|
||||||
when (@scene_list){
|
|
||||||
send_to_all_in_my_rooms($hash, $cmd);
|
|
||||||
}
|
|
||||||
default {
|
|
||||||
return "Unkown command $cmd";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
# room logic #############################################
|
|
||||||
|
|
||||||
sub send_to_all_in_my_rooms{
|
|
||||||
my ($hash, $cmd) = @_;
|
|
||||||
foreach my $room ( get_my_rooms($hash) ){
|
|
||||||
foreach my $device (VenetianBlinds::Shared::find_devices_in_room($room)) {
|
|
||||||
main::fhem("set $device $cmd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub send_to_all_in_room{
|
|
||||||
my ($cmd,$room) = @_;
|
|
||||||
foreach my $device (find_devices_in_room($room)) {
|
|
||||||
main::fhem("set $device $cmd");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub get_my_rooms{
|
|
||||||
my ($hash) = @_;
|
|
||||||
|
|
||||||
my $rooms = undef;
|
|
||||||
if (defined $hash->{rooms}){
|
|
||||||
$rooms = $hash->{rooms};
|
|
||||||
} else {
|
|
||||||
$rooms = main::AttrVal($hash->{NAME},"room",undef);
|
|
||||||
}
|
|
||||||
if (!defined $rooms) {
|
|
||||||
main::Log(1,"Error reading rooms for VenetianRoomController '$hash->{NAME}'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return split(/,/,$rooms);
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
Loading…
x
Reference in New Issue
Block a user