Tag Archives: fhem

HowTo: FHEM: Remote Control a SONOS Speaker with an Intertechno Remote

If you have a Sonos Loudspeaker, you know that you have to control them with your smartphone or tablet. But sometimes I don’t want to get my phone out to do some basic tasks like play/pause, volume up/down or next/previous track. I had an Intertechno Remote as leftover lying around and decided to use it as an remote control for my Sonos Play:1.
A prerequisite for this is, that you have already defined your Sonos Play:1 in FHEM and are able to fully control it. Because i found it very practically i decided to share the script with you.

I defined the buttons with their name staring the same and appended the row number at the end.

IT_Remote FHEM
IT_Remote FHEM

Then I wrote this script to control my Sonos Play:1:

define ny_IT_Remote1 notify IT_Remote1.* {
 my $r1 = "_1";
 my $r2 = "_2";
 my $r3 = "_3";
 my $r4 = "_4";
 my $dev = "[YOUR SONOS DEVICE]";
 my $sleepincr = 900;
 my $sleeptimer;
 my @sleeparray;
 my $sleepseconds;
 my $row = substr($NAME,-2);
 if ($row eq $r1) {
  if ($EVENT eq "off") {
   fhem("set " . $dev . " VolumeU");
  } else {
   fhem("set " . $dev . " VolumeD");
  }
 } elsif ($row eq $r2) {
  if ($EVENT eq "off") {
   fhem("set " . $dev . " Next");
  } else {
   fhem("set " . $dev . " Previous");
  }
 } elsif ($row eq $r3) {
  if ($EVENT eq "off") {
   $sleeptimer = ReadingsVal($dev,"SleepTimer","off");
   if ($sleeptimer ne "off") {
    @sleeparray = split /:/, $sleeptimer;
    $sleepseconds = int($sleeparray[0]) * 3600 + int($sleeparray[1]) * 60 + int($sleeparray[2]) + $sleepincr;
   } else {
    $sleepseconds = $sleepincr;
   }
   fhem("set " . $dev . " SleepTimer +" . $sleepseconds);
   fhem("set " . $dev . " speak +0 en Sleeptimer is set to " . int($sleepseconds / 60) . " minutes");
  } else {
   fhem("set " . $dev . " SleepTimer off");
   fhem("set " . $dev . " speak +0 en Sleeptimer disabled");
  }
 } elsif ($row eq $r4) {
  if (ReadingsVal($dev,"transportState","PLAYING") eq "PLAYING") {
   fhem("set " . $dev . " Pause");
  } else {
   fhem("set " . $dev . " Play");
  }
 }
}

This means, that i use the first row of buttons to change the volume, the second one to skip to the previous/next song and the “ALL OFF”-Button for play/pause. Also, I use the third row to set the sleep timer, the left one to turn the sleep timer off and the right one to increase it for 900($sleepincr) seconds. I like to know if the sleep timer was set correctly, so I use sonosspeak to let the speaker tell me the minutes left on the sleep timer.

Howto: FHEM: Kindle Display: Refresh on state/value change

I’m using FHEM a lot these days, so i decided to buy an Amazon Kindle and make a status display with it.
The KindleDisplay-Article on fhemwiki.de helped me a lot doing these thing, the only downside was that it would only update every couple of minutes, which is a bit boring.

So there are two problems in my way to have a realtime Kindle-Display:
1. Update the Status-Image with FReplacer everytime something shown on the display changes.
2. Tell the Kindle to fetch a new picture.

The first problem is solved pretty easy, you just define a dummy-variable to safe the old values in there:

define kindledisplayvalues dummy

Then make an at to check every couple seconds (i do this every 5 seconds) if something has changed:

define kindlerefresh at +*00:00:05 {
 my $display="[PUT THE NAME OF YOUR FREPLACER HERE]";
 my $oldvals="kindledisplayvalues";
 my $attrvalue="";
 my $valuename="";
 my $attr="";
 my @attrs=split / /, AttrVal($display,"userattr","none");
 my @attrvalues="";
 my $updatedisplay="no";
 if ($attrs[0] eq "none") {
  return;
 }
 foreach $attr (@attrs) {
  if (index($attr, "Reading") ne -1) {
   $valuename=AttrVal($display,$attr,"none");
   if (index($valuename, $display . ":LastUpdate") eq -1) {
    if (index($valuename, ":") ne -1) {
     @attrvalues=split /:/, $valuename;
     $attrvalue=ReadingsVal($attrvalues[0],$attrvalues[1],"none");
    } else {
     $attrvalue=Value($valuename);
    }
    if (ReadingsVal($oldvals,$valuename,"none") ne $attrvalue) {
     fhem("setreading " . $oldvals . " " . $valuename . " " . $attrvalue);
     $updatedisplay="yes";
    }
   }
  }
 }
 if ($updatedisplay eq "yes") {
  Log 2, "Updating Kindle Display";
  fhem("set ". $display ." ReplaceNow");
 }
}

Only downside: If you have “RepXXExpr” atrributes in your FReplacer defined, you should consider defining “RepXXReading” attributes (just use some numbers where there is no “RegXXRegex”) with the devicenames used in the expr, so that the Script checks those too.

First problem solved.

Second Problem:

This is a bit of fiddling around, but still doable.
First of all, you copy the ssh public key of your fhem-user to the Kindle.

sudo scp /opt/fhem/.ssh/id_rsa.pub root@<ip_of_your_kindle>:/mnt/us/usbnet/etc/authorized_keys

After you did that, FHEM should be able to Login on the Kindle via SSH without putting in a password.

Now we tell the FReplacer to Login on the Kindle and Fetch a new Image when it is finished:

attr kindledisplay PostCommand convert /opt/fhem/www/images/KindleDisplay.svg -type GrayScale -depth 8 /opt/fhem/www/images/KindleDisplay.png 2>/dev/null;;ssh root@<ip_of_your_kindle> '/mnt/us/extensions/onlinescreensaver/bin/update.sh' 2>/dev/null &

A small hint: If you don’t want the display to flicker when it gets updated: Search for “eips” in the update.sh and remove the “-f” behind it, the Kindle does only incremental updates then.
I copied the update.sh to smallupdate.sh, removed the “-f” and call the “smallupdate.sh” now with the FReplacer. This means it makes an incremental displayrefresh on change and i let it still call the normal update.sh every hour, so i don’t get any ghosting on the display.

HowTo: FHEM: Check if all windows are closed when leaving

As I’m always worried if all my windows are closed when I leave my house, I decided to let FHEM check if they are closed.
My windows are monitored with MAX! Shutter contacts, which are compatible with FHEM, so it can always see if they are open or closed.

To do the job, I wrote a script which sends me a push-message, stating if all windows are closed and if not, telling me which windows are open. In FHEM you put that script into a notify, with this notify we can then trigger the script whenever we want. For your setup you need to change the device names and add/delete devices in the script. Things you have to change are in bold and brackets:

define checkWindows notify checkWindows {
 my $winKitchen=Value("[Name of the Shutter contact in the Kitchen]");
 my $winSleepingroom=Value("[Name of the Shutter contact in the Sleepingroom]");
 my $winLivingroom=Value("[Name of the Shutter contact in the Livingroom]");
 my $winBath=Value("[Name of the Shutter contact in the Bathingroom]");
 if (($winKitchen ne "closed") || ($winSleepingroom ne "closed") || ($winLivingroom ne "closed") || ($winBath ne "closed")) {
  my $openWindows="";
  if ($winKitchen ne "closed") { $openWindows="Kitchen "; }
  if ($winSleepingroom ne "closed") { $openWindows=$openWindows . "Sleepingroom "; }
  if ($winLivingroom ne "closed") { $openWindows=$openWindows . "Livingroom "; }
  if ($winBath ne "closed") { $openWindows=$openWindows . "Bathingroom"; }
  fhem("set push msg 'ATTENTION: Window(s) open!' '" . $openWindows . "' '[Push Device]' 1 ''");
 } else {
  fhem("set push msg 'SAVE' 'All windows are closed.' '[Push Device]' -1 ''");
 }
}

After we created the notify we need to trigger the script when we leave. To check if you are at home or not, we can use presence and check the bluetooth address of our smartphone, because we always take our smartphone with us :). Just fill in the bluetooth address of your smartphone instead of the bold text:

define Smartphone PRESENCE local-bluetooth [Bluetooth address of your Smartphone]
attr Smartphone absenceThreshold 2

For example:

define Smartphone PRESENCE local-bluetooth 01:23:45:67:89:AB
attr Smartphone absenceThreshold 2

The absenceThreshold set to 2 tells FHEM to check twice before saying that your smartphone is away.
Now we need to trigger our checkWindows script as soon as our smartphone goes absent with a DOIF:

define di_WindowChecker DOIF ([Smartphone] eq "absent") (trigger checkWindows)

That’s it! Now you always get a message telling you the state of your windows when leaving!

HowTo: Climate Control at home with FHEM

As I have many houseautomation devices at home, I thought it would be a good idea to use them to tell me when I should open my windows and when to close them again.
For this little project I used the Netatmo Weatherstation with an additional Sensor for a second room to readout the temperature, dewpoint and co2 of my rooms and temperature and dewpoint outside. Additionally I use my eq-3 MAX! heating control for the actual set temperature at the radiators and the state (open/closed) of my windows in that rooms.
First thing i though about was, what do I want to have in my room, so I made a mindmap that shows what conditions the room should have in best case.

fhem_cc_room_conditions

Then I thought about what values I get from my devices:

fhem_cc_values

Based on this two things, I considered which conditions I would open the window and at what conditions I would close it:

fhem_cc_conditions

To see the relation between my values and my conditions I drew some arrows in it:

fhem_cc_conditions_with_arrows

The advantage of using the dewpoint is, that humidity is only relative humidity, whereas the dewpoint gives a clue about the absolute humidity of the air. The higher the dewpoint is, the more water is in the air. You should try to get your dewpoint as low as possible and never get with your room temperature near or under the dewpoint.

Hint: If your devices don’t have the dewpoint value but temperature and humidity, you can add the dewpoint with following command:

define dew_all dewpoint dewpoint .* temperature humidity dewpoint

The conditions to open my windows are pretty clear:
– Keep CO2 low
– Keep the air as dry as possible
– Try to get to my set temperature

So all the Script does is, if you are at home, send you a push message on your phone to open or close a certain window. Also it sets a dummy variable which you can use for visualisation in the fhem webinterface. The states are “OK” if no action is needed, “open” if you need to open a window and “close” if you should close the window again. Also it will create dummy variables for the maximum allowed co2, the dew point offset (because the air near the window and walls are a bit colder, so you need to use a lower value), the set temperature for the summer and an offset for the set temperature.

Here is the script I wrote, you need to fill in your own values at the bold written places which are also marked with brackets.

define ClimateControl at +*00:05:00 { 
 no warnings 'redefine';
 my $outTemp=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","temperature",0);
 my $outDew=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","dewpoint",100);
 
 my $sleepTemp=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","temperature",0);
 my $sleepDew=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","dewpoint",0);
 my $sleepCO2=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","co2",0);
 my $sleepWin=ReadingsVal("[MAX_XXXXXX]","state","none");
 my $sleepSetTemp=ReadingsVal("[MAX_XXXXXX]","desiredTemperature",20);
 my $sleepVar="[Sleepingroom]";

 my $livingTemp=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","temperature",0);
 my $livingDew=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","dewpoint",0);
 my $livingCO2=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","co2",0);
 my $livingWin=ReadingsVal("[MAX_XXXXXX]","state","none");
 my $livingSetTemp=ReadingsVal("[MAX_XXXXXX]","desiredTemperature",20);
 my $livingVar="[Livingroom]";

 $sleepSetTemp = SummerSetTemp($sleepVar,$sleepSetTemp);
 $livingSetTemp = SummerSetTemp($livingVar,$livingSetTemp);
 CheckRoom($outTemp, $outDew, $sleepTemp, $sleepDew, $sleepCO2, $sleepWin, $sleepSetTemp, $sleepVar);
 CheckRoom($outTemp, $outDew, $livingTemp, $livingDew, $livingCO2, $livingWin, $livingSetTemp, $livingVar);

 sub CheckRoom {
  die "Too many arguments for subroutine" unless @_ <= 8;
  die "Too few arguments for subroutine" unless @_ >= 8;

  my $outTemp=$_[0];
  my $outDew=$_[1];
  my $roomTemp=$_[2];
  my $roomDew=$_[3];
  my $roomCO2=$_[4];
  my $roomWin=$_[5];
  my $roomSetTemp=$_[6];
  my $roomVar=$_[7];
  my $roomVarState=ReadingsVal($roomVar,"state","undefined");
  my $dewOffset=ReadingsNum($roomVar . "DewOffset","state",999);
  my $setTempOffset=ReadingsNum($roomVar . "SetTempOffset","state",999);
  my $CO2max=ReadingsNum($roomVar . "maxCO2","state",9999);

  if ($roomVarState eq "undefined") {
   fhem("define " . $roomVar . " dummy");
   fhem("set " . $roomVar . " OK");
   fhem("attr " . $roomVar . " stateFormat Window: state");
  }
  if ($dewOffset eq 999) {
   fhem("define " . $roomVar . "DewOffset dummy");
   fhem("set " . $roomVar . "DewOffset 3");
   fhem("attr " . $roomVar . "DewOffset stateFormat state °C");
   $dewOffset = 3;
  }
  if ($setTempOffset eq 999) {
   fhem("define " . $roomVar . "SetTempOffset dummy");
   fhem("set " . $roomVar . "SetTempOffset 1");
   fhem("attr " . $roomVar . "SetTempOffset stateFormat state °C");
   $setTempOffset = 1;
  }
  if ($CO2max eq 9999) {
   fhem("define " . $roomVar . "maxCO2 dummy");
   fhem("set " . $roomVar . "maxCO2 1000");
   fhem("attr " . $roomVar . "maxCO2 stateFormat state ppm");
   $CO2max = 1000;
  }

  if (($outDew < $roomDew) && ($roomDew > ($roomTemp-$dewOffset))) {
   DoWinOpen($roomVar,$roomWin);
  }
  if ($roomCO2 > $CO2max) {
   DoWinOpen($roomVar,$roomWin);
  }
  if ($roomDew > $outDew) {
   if (($outTemp < $roomTemp) && ($roomSetTemp < $roomTemp)) {
    DoWinOpen($roomVar,$roomWin);
   }
   if (($outTemp > $roomTemp) && ($roomSetTemp > $roomTemp)) {
    DoWinOpen($roomVar,$roomWin);
   }
  }
  if (($roomCO2 < $CO2max) && ($roomDew < ($roomTemp-$dewOffset))) {
   if (($outTemp <= $roomTemp) && (($roomSetTemp-$setTempOffset) >= $roomTemp)) {
    DoWinClose($roomVar,$roomWin);
   }
   if (($outTemp >= $roomTemp) && ($roomSetTemp <= $roomTemp)) {
    DoWinClose($roomVar,$roomWin);
   }
  }
 } 
 sub DoWinOpen {
  die "Too many arguments for subroutine" unless @_ <= 2;
  die "Too few arguments for subroutine" unless @_ >= 2;
  my $roomVar=$_[0];
  my $winState=$_[1];
  my $roomState=ReadingsVal($roomVar,"state","none");
  if ($winState eq "opened") {
   fhem("set " . $roomVar . " OK");
  }
  if (($roomState ne "open") && ($winState eq "closed")) {
   fhem("set " . $roomVar . " open");
   if (ReadingsVal("[PRESENCE]","state","absent") ne "absent") {
    fhem("set push msg '" . $roomVar . "' 'Open the Window' '' 1 ''");
   }
  }
 }
 sub DoWinClose {
  die "Too many arguments for subroutine" unless @_ <= 2;
  die "Too few arguments for subroutine" unless @_ >= 2;
  my $roomVar=$_[0];
  my $winState=$_[1];
  my $roomState=ReadingsVal($roomVar,"state","none");
  if ($winState eq "closed") {
   fhem("set " . $roomVar . " OK");
  }
  if (($roomState ne "close") && ($winState eq "opened")) {
   fhem("set " . $roomVar . " close");
   if (ReadingsVal("[PRESENCE]","state","absent") ne "absent") {
    fhem("set push msg '" . $roomVar . "' 'Close the Window' '' 1 ''");
   }
  }
 }
 sub SummerSetTemp {
  die "Too many arguments for subroutine" unless @_ <= 2;
  die "Too few arguments for subroutine" unless @_ >= 2;
  my $roomVar=$_[0];
  my $roomSetTemp=$_[1];
  if ($roomSetTemp eq "off") {
   $roomSetTemp=ReadingsNum($roomVar . "SummerTemp","state",999);
   if ($roomSetTemp eq 999) {
    fhem("define " . $roomVar . "SummerTemp dummy");
    fhem("set " . $roomVar . "SummerTemp 20");
    fhem("attr " . $roomVar . "SummerTemp stateFormat state °C");
    $roomSetTemp=20;
   }
  }
  return $roomSetTemp;
 }
}