A collection of bits of fhem code.

Introduction
Yahoo weather
Twilight
MAX! Devices HomeMatic devices RFXtrx and associated devices (HomeEasy) 1-wire 99_myUtils.pm
More complex stuff

Introduction

When I started using fhem I searched for example configuration files and other bits to help me understand and set up my own system.
I have borrowed and modified bits from many sources, so thanks to those who have made examples available.

Apologies for formatting on this page but many of the code lines are very long.

I have added this page in the hope it may help others in a similar situation. It's all pretty basic stuff.
Most are generic and will work on any platform but there may be a few specific to a setup on a Raspberry Pi.
Apart from the first examples I don't intend adding more than a few comments, search the forum and wiki for more information.

A first glance at this page may give the impression that a lot of typing is involved in order to implement any of the suggestions here. This is not the case. Apart from the initial definitions most of the rest is simply a choice or a word or two in an attribute.

I have highlighted settings such as IP addresses as these will depend on your own system setup as will device names.

An initial install of fhem will provide a default system with a default fhem.cfg.
As you add devices they will be included in the running fhem.cfg automatically in many cases but will often require additional attributes, log files and plot sections. Note that these will not be saved to the fhem.cfg file on disk!

When starting out do all your configuration from the fhem web pages, save each change once you know everything is working, as you progress.
Saving is not automatic by design, allowing you to try things out. If they don't work, reload fhem.cfg to revert any changes (type help in an fhem web page to see available commands).
As you save fhem will check for errors and list any it finds.

Don't worry if you are presented with a long list of errors as often happens in situations like this.
The first reported is the most important and will often be the cause of the rest. Fix that first one and try saving again.

Editing fhem files directly is not a good idea, even if you really know what you are doing. It can so easily result in an unusable configuration. You will then likely have to restore from backup - if you have one!
In short do not even try, you will mess it up sooner or later!
I have been there! I do all my editing from the web pages.

See also the fhem Howto and FAQ.
Some code examples are taken from my config files and may not appear exactly as written in the fhem configuration web pages as some will be modified by escape characters (eg. ;;,, \@ and \),
see Fhem command types.
NB. see relevent detail for notify command in fhem 5.7 onwards.

Some examples, eg. the readingsGroup definitions, are multi-line. I have split these using the \. continuation character for clarity.

They will work as listed but they can be reformatted as a single line or with several parts to a line and the lines joined with a \ character.



Yahoo weather

For those starting out or wanting to take a look at a working fhem let's start with a weather forecast. We can do this with no additional hardware or physical devices.

Commands are entered in the command box at the top of any fhem web page accessed by pointing a browser to port 8083 of whatever IP address your fhem is running on eg. http://192.168.0.35:8083.
A command is not actually sent until you hit the Enter/Return key.

We first need to define the parameters for the Weather module (see fhem commandref for details) and should be entered in the command box eg.

define myWeather Weather 22489156 3600 en

22489156 is the Yahoo WOEID for Stansted Airport (UK), you should be able to find a location closer to you.

Once we have entered the above we need to set at least a room attribute so that we can start to organise our setup. Let's put it in the room WeatherForecast.
At the bottom of the new myWeather page you will see the attr section, by default you will probably see attr myWeather room and an empty box to the right, click in the box, after a little time you will see a list of already defined rooms and an empty box at the bottom, type WeatherForecast here then click the attr button.
You should now see WeatherForecast amongst the links on the left hand side of the page (these links are what are known as rooms in fhem).

The code for this in fhem.cfg will be

attr myWeather room WeatherForecast

we could simply have entered it as a command (you may have to if you are using an older version of fhem that does not have this feature in the attributes section).

If you lose the page click the Everything link, or for your new myWeather page go to
http://192.168.0.35:8083/fhem?detail=myWeather

Once you are happy don't forget to Save config.

Any of the data items that appear on our new page can be used elsewhere in fhem for control or other purposes.

Often fhem will create a log file as you create new device, if so there will be a link in the Probably associated with section of the page. If not

define FileLog_myWeather FileLog ./log/myWeather-%Y-%m.log myWeather

set the attr logtype to text and room to WeatherForecast too for now using the attr button.

Now for some icons

define myWeather_weblink weblink htmlCode {WeatherAsHtml("myWeather")}

and again set room to WeatherForecast.

Before we move on let's tidy up and add comments in fhem.cfg Click Edit files then fhem.cfg. Your config file should appear in an editable window with your newly entered code at the bottom like this

define myWeather Weather 22489156 3600 en
attr myWeather room WeatherForecast

define FileLog_myWeather FileLog ./log/myWeather-%Y-%m.log myWeather
attr FileLog_myWeather logtype text
attr FileLog_myWeather room Logs

define myWeather_weblink weblink htmlCode {WeatherAsHtml("myWeather")}
attr myWeather_weblink room WeatherForecast

Add comment lines (comments start with # character) and empty lines to break that block up and make things clearer so you end up with something like this

NB. this is the only time I edit my own config. Anything else should be considered unwise and dangerous! Do not do it, you will break something sooner or later!

#########################################################
####    Yahoo weather
#########################################################

define myWeather Weather 22489156 3600 en
attr myWeather room WeatherForecast

####    Logfile all Weather Data
define FileLog_myWeather FileLog ./log/myWeather-%Y-%m.log myWeather
attr FileLog_myWeather logtype text
attr FileLog_myWeather room Logs

####    Add weather icons
define myWeather_weblink weblink htmlCode {WeatherAsHtml("myWeather")}
attr myWeather_weblink room WeatherForecast

When done click the Save fhem.cfg button. fhem will check the file as it saves so if you have made a mistake you may see an error, go back and correct it then save again.



Twilight

Another that doesn't require any physical hardware with its associated log.

define myTwilight Twilight 51.8875 0.2606 1 22489156

define FileLog_myTwilight FileLog ./log/Twilight-%Y-%m.log myTwilight

Latitude and longitude are for Stansted Airport again and we use the same Yahoo WOEID. Set the room to WeatherForecast for both and set attr logtype to text for the log
(generally log files should be typed text), so with added comments

#########################################################
####    Twilight
#########################################################

define myTwilight Twilight 51.8875 0.2606 1 22489156
attr myTwilight room WeatherForecast

define FileLog_myTwilight FileLog ./log/Twilight-%Y-%m.log myTwilight
attr FileLog_myTwilight logtype text
attr FileLog_myTwilight room WeatherForecast



MAX! Cube

My first physical device was a MAX! Cube. It is necessary to create the initial define for it to recognised

define ml MAXLAN 192.168.0.39

My edited result looks like

#########################################################
####    MAX! CUBE
#########################################################

define ml MAXLAN 192.168.0.39 ondemand
attr ml dummy 1
attr ml icon my_cube
attr ml room System
attr ml set-clock-on-init 1
attr ml timezone GMT-BST

The comment lines simply identify and break up fhem.cfg into manageable sections. They do have to be added by direct editing but please do this from the Edit files web page.

I have named my Cube ml as you can see. Throughout this page you may notice entries such as the icon my_cube I have a habit of saving important things such as icons and gplot files under a new, unique name so they don't get overwritten by later updates. Refer to the fhem commandref for the various attributes and other settings.

The Cube has an associated log

####   MAX! CUBE logs
define FileLog_ml FileLog ./log/ml-%Y-%m.log ml
attr FileLog_ml logtype text
attr FileLog_ml room Logs



MAX! devices

I have a number of MAX! radiator thermostats, door/window contacts and an Eco Switch. I have not renamed any of these devices but used the alias attribute instead. Typical config for each is

#########################################################
####    Bathroom MAX_075bd4
#########################################################

define MAX_075bd4 MAX HeatingThermostat 075bd4
attr MAX_075bd4 IODev ml
attr MAX_075bd4 alias Bath_Rad
attr MAX_075bd4 event-on-change-reading .*
attr MAX_075bd4 icon my_heating_automatic
attr MAX_075bd4 keepAuto 1
attr MAX_075bd4 room Bathroom

####    Bathroom MAX_075bd4 logs
define FileLog_MAX_075bd4 FileLog  ./log/MAX_075bd4-%Y-%m.log MAX_075bd4
attr FileLog_MAX_075bd4 logtype text
attr FileLog_MAX_075bd4 room Logs

####    Bathroom MAX_075bd4 Plot
define wl_MAX_075bd4 SVG FileLog_MAX_075bd4:my_max_temp:CURRENT
attr wl_MAX_075bd4 label "Bathroom Temperature Min $data{min1}, Max $data{max1}, Last $data{currval1}"
attr wl_MAX_075bd4 room Bathroom,RadiatorPlots
attr wl_MAX_075bd4 title "Bathroom temperature Min $data{min1}, Max $data{max1}, Last $data{currval1}, Curr $data{currval2}°C, Valve $data{currval3}%"

####    ShutterContact MAX_04d87a
define MAX_04d87a MAX ShutterContact 04d87a
attr MAX_04d87a IODev ml
attr MAX_04d87a alias Bath_WindSw
attr MAX_04d87a devStateIcon opened:my_window_1w_open@red closed:my_window_1w@green
attr MAX_04d87a event-on-change-reading .*
attr MAX_04d87a icon my_window_open
attr MAX_04d87a room Bathroom

####    ShutterContact MAX_04d87a logs
define FileLog_MAX_04d87a FileLog ./log/MAX_04d87a-%Y-%m.log MAX_04d87a
attr FileLog_MAX_04d87a logtype text
attr FileLog_MAX_04d87a room Logs

#########################################################
####    Eco Switch
#########################################################

define MAX_00c323 MAX PushButton 00c323
attr MAX_00c323 IODev ml
attr MAX_00c323 alias Eco_Sw
attr MAX_00c323 event-on-change-reading .*
attr MAX_00c323 icon my_eco
attr MAX_00c323 room Hall
attr MAX_00c323 verbose 4

####    Eco Switch Logs
define FileLog_MAX_00c323 FileLog ./log/MAX_00c323-%Y-%m.log MAX_00c323
attr FileLog_MAX_00c323 logtype text
attr FileLog_MAX_00c323 room Logs



HomeMatic devices

In order to control my pump and boiler I needed a switch. I chose an HM-LC-SW2-FM along with a HomeMatic USB Configuration Adapter.
To use the USB adapter you will need to follow the instructions in the fhem wiki The resulting installed binary is loaded at startup using the runit application. It is run now in current installs from the /etc/init.d/fhem file.

The current debian fhem package includes a section within its init script to run the binary - but you have to edit the script to enable the feature.

It is possible to update the firmware on the USB adapter from linux but I don't advise this unless you really know what you are doing!

#########################################################
####    HM-CFG-USB-2
#########################################################

define hmusb HMLAN 127.0.0.1:1234
attr hmusb event-on-change-reading .*
attr hmusb hmId 0000D0
attr hmusb hmLanQlen 1_min
attr hmusb icon my_hmusb
attr hmusb room System
attr hmusb wdTimer 25

define FileLog_hmusb FileLog ./log/hmusb-%Y-%m.log hmusb:.*
attr FileLog_hmusb logtype text
attr FileLog_hmusb room Logs

#########################################################
####    HM_HM_LC_SW2_FM
#########################################################

define Switch_00 CUL_HM 1F6F42
attr Switch_00 .devInfo 020200
attr Switch_00 .stc 10
attr Switch_00 autoReadReg 4_reqStatus
attr Switch_00 expert 2_full
attr Switch_00 firmware 1.9
attr Switch_00 model HM-LC-SW2-FM
attr Switch_00 room Heat_Control
attr Switch_00 serialNr KEQ0000005
attr Switch_00 subType switch
attr Switch_00 webCmd getConfig

define FileLog_Switch_00 FileLog ./log/Switch_00-%Y-%m.log Switch_00
attr FileLog_Switch_00 logtype text
attr FileLog_Switch_00 room Logs

####    Switch 1
define Switch_01 CUL_HM 1F6F4201
attr Switch_01 alias Pump_switch
attr Switch_01 devStateIcon on:sani_pump@red off:sani_pump.@grey
attr Switch_01 event-on-change-reading .*
attr Switch_01 expert 1
attr Switch_01 icon my_HeatingOn
attr Switch_01 model HM-LC-SW2-FM
attr Switch_01 peerIDs 00000000,
attr Switch_01 room Heat_Control
attr Switch_01 webCmd toggle:on:off:statusRequest

define FileLog_Switch_01 FileLog ./log/Switch_01-%Y-%m.log Switch_01
attr FileLog_Switch_01 logtype text
attr FileLog_Switch_01 room Logs

####    Switch 1 Plot
define wl_Switch_01 SVG FileLog_Switch_01:my_switches:CURRENT
attr wl_Switch_01 label "Pump switch state"
attr wl_Switch_01 room Heat_Control
attr wl_Switch_01 title "Pump switch state, Current $data{currval1}"

####    Switch 2
define Switch_02 CUL_HM 1F6F4202
attr Switch_02 alias Boiler_switch
attr Switch_02 devStateIcon on:sani_boiler_temp@red off:sani_boiler_temp@grey
attr Switch_02 event-on-change-reading .*
attr Switch_02 expert 1
attr Switch_02 icon my_HeatingReg
attr Switch_02 model HM-LC-SW2-FM
attr Switch_02 peerIDs 00000000,
attr Switch_02 room Heat_Control
attr Switch_02 webCmd toggle:on:off:statusRequest

define FileLog_Switch_02 FileLog ./log/Switch_02-%Y-%m.log Switch_02
attr FileLog_Switch_02 logtype text
attr FileLog_Switch_02 room Logs

####    Switch 2 Plot
define wl_Switch_02 SVG FileLog_Switch_02:my_switches:CURRENT
attr wl_Switch_02 label "Boiler switch state"
attr wl_Switch_02 room Heat_Control
attr wl_Switch_02 title "Boiler switch state, Current $data{currval1}"

The switches have been renamed from their initial long defaults. Note that this two gang switch appears as three devices, the first is the device itself and then its two contained sub-devices.

I have an HM-PB-2-WM55 wall switch too

#########################################################
#####    HM Wall Switch
#########################################################

define HomeAway_Switch CUL_HM 19D090
attr HomeAway_Switch IODev hmusb
attr HomeAway_Switch autoReadReg 4_reqStatus
attr HomeAway_Switch devStateIcon Btn_02.Short.*:status_away_1@red Btn_01.Short.*:status_available@black
attr HomeAway_Switch expert 2_full
attr HomeAway_Switch firmware 1.0
attr HomeAway_Switch icon my_switch
attr HomeAway_Switch model HM-PB-2-WM55
attr HomeAway_Switch room Hall
attr HomeAway_Switch serialNr JEQ0005264
attr HomeAway_Switch subType pushButton
attr HomeAway_Switch webCmd getConfig:clear msgEvents

define FileLog_HomeAway_Switch FileLog ./log/HomeAway_Switch-%Y-%m.log HomeAway_Switch
attr FileLog_HomeAway_Switch logtype text
attr FileLog_HomeAway_Switch room Logs

define Btn_02 CUL_HM 19D09001
attr Btn_02 model HM-PB-2-WM55
attr Btn_02 peerIDs 00000000,

define Btn_01 CUL_HM 19D09002
attr Btn_01 model HM-PB-2-WM55
attr Btn_01 peerIDs 00000000,

and an HM-WDS100-C6-O WeatherStation

#########################################################
####   WeatherStation
#########################################################

define WeatherStation CUL_HM 1FCD3A
attr WeatherStation .devInfo 3F0100
attr WeatherStation .stc 70
attr WeatherStation IODev hmusb
attr WeatherStation actCycle 000:10
attr WeatherStation actStatus alive
attr WeatherStation autoReadReg 4_reqStatus
attr WeatherStation expert 2_full
attr WeatherStation firmware 1.4
attr WeatherStation model HM-WDS100-C6-O
attr WeatherStation room WeatherStation
attr WeatherStation serialNr KEQ0241679
attr WeatherStation subType THSensor

define FileLog_WeatherStation FileLog ./log/WeatherStation-%Y-%m.log WeatherStation:.*
attr FileLog_WeatherStation logtype text
attr FileLog_WeatherStation room Logs

define FileLog_ActionDetector FileLog ./log/ActionDetector-%Y-%m.log ActionDetector
attr FileLog_ActionDetector logtype text
attr FileLog_ActionDetector room Logs



RFXtrx and associated devices

I obtained the RFXtrx in the hope that I could use the HomeEasy/Byron range of wireless devices commonly available at reasonable cost in the UK. I have found that HomeEasy socket adapters work well as do the PIRs (Elro but branded HomeEasy).

Note that all HomeEasy transmitter devices generate a lot of traffic recognised as several different protocols. It is necessary to determine which is the most reliable and use hide and/or ignore attributes for the rest.

First the RFXtrx

#########################################################
####   RFXTRX
#########################################################

define RFXTRXUSB TRX /dev/ttyUSB0@38400
attr RFXTRXUSB icon my_rfxtrx
attr RFXTRXUSB room System
attr RFXTRXUSB rssi 1

A HomeEasy HE830S socket adapter

#########################################################
####   HE830S Socket 02
#########################################################

define Socket_02 TRX_LIGHT AC 000043b802 light
attr Socket_02 IODev RFXTRXUSB
attr Socket_02 alias Socket-02-LR
attr Socket_02 comment Socket 02 HE830S
attr Socket_02 devStateIcon on:my_UK_Switch.on off:my_UK_Switch.off
attr Socket_02 icon my_socket_UK
attr Socket_02 room TRX_LIGHT
              
define FileLog_Socket_02 FileLog ./log/Socket_02-%Y-%m.log Socket_02
attr FileLog_Socket_02 logtype text
attr FileLog_Socket_02 room Logs

A HomeEasy HE330S socket adapter

#########################################################
####   HE330S Socket 21
#########################################################

define Socket_21 TRX_LIGHT AC 0056ccfe01 light
attr Socket_21 IODev RFXTRXUSB
attr Socket_21 alias Socket-21-Porch
attr Socket_21 comment Socket 21 HE330S
attr Socket_21 devStateIcon on:my_UK_Switch.on off:my_UK_Switch.off
attr Socket_21 icon my_socket_UK
attr Socket_21 room TRX_LIGHT

define FileLog_Socket_21 FileLog ./log/Socket_21-%Y-%m.log Socket_21
attr FileLog_Socket_21 logtype text
attr FileLog_Socket_21 room Logs

A HomeEasy/Elro internal HE851 PIR

#########################################################
####   PIR Hall
#########################################################

define PIR_Hall TRX_LIGHT HOMEEASY 0001880c02 light
attr PIR_Hall IODev RFXTRXUSB
attr PIR_Hall comment Settings 3m 10min H
attr PIR_Hall devStateIcon on:my_motion_detector@red off:my_motion_detector@black
attr PIR_Hall event-min-interval state:900
attr PIR_Hall icon my_motion_detector
attr PIR_Hall room TRX_LIGHT

define FileLog_PIR_Hall FileLog ./log/PIR_Hall-%Y-%m.log PIR_Hall
attr FileLog_PIR_Hall logtype text
attr FileLog_PIR_Hall room Logs

A HomeEasy/Elro external HE861 PIR

#########################################################
####   PIR 1 External
#########################################################

define PIR_disabled TRX_LIGHT HOMEEASY 0000dc8802 light
attr PIR_disabled IODev RFXTRXUSB
attr PIR_disabled comment PIR 1 Ext
attr PIR_disabled devStateIcon on:my_IR@red off:my_IR@black
attr PIR_disabled icon my_IR
attr PIR_disabled room TRX_LIGHT

define FileLog_PIR_disabled FileLog ./log/PIR_disabled-%Y-%m.log PIR_disabled
attr FileLog_PIR_disabled logtype text
attr FileLog_PIR_disabled room Logs

define wl_PIR_Garage SVG FileLog_PIR_Garage:my_piri:CURRENT
attr wl_PIR_Garage room TRX_LIGHT

I have stopped using the HomeEasy wall switches, I found I could not rely on them working as expected - but that may just be my setup. Here is an example anyway

#########################################################
####   HomeEasy Wall Switch
#########################################################

define TRX_HOMEEASY_0000215003 TRX_LIGHT HOMEEASY 0000215003 light
attr TRX_HOMEEASY_0000215003 IODev RFXTRXUSB
attr TRX_HOMEEASY_0000215003 room hidden

define FileLog_TRX_HOMEEASY_0000215003 FileLog ./log/TRX_HOMEEASY_0000215003-%Y-%m.log TRX_HOMEEASY_0000215003
attr FileLog_TRX_HOMEEASY_0000215003 disable 1
attr FileLog_TRX_HOMEEASY_0000215003 logtype text
attr FileLog_TRX_HOMEEASY_0000215003 room hidden




1-wire

I have a Sheepwalk Electronics RPI3 1-wire interface.

#########################################################
####   1-Wire server
#########################################################

define myLocalOWServer OWServer localhost:4304
attr myLocalOWServer room OWDevice

and a number of 1-wire devices. Availability of 1-wire devices seems far more limited in the UK than the US or Europe.
I am not going to list all I have but may add a few more later.
A DS18B20 temperature sensor

#########################################################
####   SWE2
#########################################################

define SWE2 OWDevice 28.898970040000 180
attr SWE2 IODev myLocalOWServer
attr SWE2 comment SWE2
attr SWE2 model DS18B20
attr SWE2 room OWDevice
attr SWE2 stateFormat { sprintf( "%.1f", ReadingsValSWE2", "temperature", 0 ) )."°C" }
attr SWE2 userReadings T { int ( 10 * ReadingsVal( "SWE2", "temperature", 0 ) + 0.5 ) / 10 }

define FileLog_SWE2 FileLog ./log/SWE2-%Y-%m.log SWE2
attr FileLog_SWE2 logtype text
attr FileLog_SWE2 room Logs

define wl_SWE2 SVG FileLog_SWE2:my_DS1820:CURRENT
attr wl_SWE2 room OWDevice

A Sheepwalk Electronics SWE3.
This is a more complex config. The SWE3 is associated with a DS18B20 temperature sensor, it also relies on the dewpoint module, modified to generate absolute humidity (absFeuchte) as well as relative.

#########################################################
####   SWE3
#########################################################

define SWE3 OWDevice 26.E8DE55010000 180
attr SWE3 IODev myLocalOWServer
attr SWE3 event-min-interval .*:3600
attr SWE3 event-on-change-reading .*
attr SWE3 model DS2438
attr SWE3 room OWDevice
attr SWE3 stateFormat { sprintf( "T: %.1f H: %.0f D: %.1f A: %.1f", ReadingsVal( "SWE3", "temperature_T", 0), ReadingsVal( "SWE3", "humidity", 0), ReadingsVal( "SWE3", "dewpoint", 0), ReadingsVal( "SWE3", "absFeuchte", 0 ) ) }
attr SWE3 userReadings humidity { int( 10 * ( ( ReadingsVal( "SWE3", "VAD", 0 ) * 161.29 / ReadingsVal( "SWE3", "VDD", 0 ) - 25.8065 ) / ( 1.0546 - 0.00216 * ( ReadingsVal( "SWE3_T", "T", 0 ) + 0.0 ) ) ) + 0.5 ) / 10 }, temperature_T { int( 10 * ReadingsVal( "SWE3_T", "temperature", 0 ) + 0.5 ) / 10 }

define FileLog_SWE3 FileLog ./log/SWE3-%Y-%m.log SWE3
attr FileLog_SWE3 logtype text
attr FileLog_SWE3 room Logs

define wl_SWE3 SVG FileLog_SWE3:my_DS2438:CURRENT
attr wl_SWE3 label "rHum: Last: $data{currval1}% - aHum: Last: $data{currval4}% - Dewpoint: Last: $data {currval2}°C - Temp: Min: $data{min3}, Max: $data{max3}, Last: $data{currval3}°C"
attr wl_SWE3 room OWDevice

define SWE3_T OWDevice 28.85EEBD030000 180
attr SWE3_T IODev myLocalOWServer
attr SWE3_T event-min-interval .*:3600
attr SWE3_T event-on-change-reading .*
attr SWE3_T model DS18B20
attr SWE3_T room OWDevice
attr SWE3_T stateFormat { sprintf( "%.1f", ReadingsVal( "SWE3_T", "temperature", 0 ) ) . "°C" }
attr SWE3_T userReadings T { int ( 10 * ReadingsVal( "SWE3_T", "temperature", 0 ) + 0.5 ) / 10 }

define FileLog_SWE3_T FileLog ./log/SWE3_T-%Y-%m.log SWE3_T
attr FileLog_SWE3_T logtype text
attr FileLog_SWE3_T room Logs



99_myUtils.pm

Additional code can be entered in fhem.cfg but another way is to add functions to your own 99-myUtils.pm that is placed in the /opt/fhem/FHEM/ directory. Create this following the instructions given at the end of /opt/fhem/FHEM/99_Utils.pm.

Now we only need the function call in fhem.cfg so let's look at a simple example, sending an email on a specific event. This is described in more detail in the wiki.

Add this code to 99-myUtils.pm noting the warnings about where to place it and substituting your details where appropriate.

######## DebianMail  Mail sending from the RPi  ############
sub
DebianMail
{
 my $rcpt = shift;
 my $subject = shift;
 my $text = shift;
 my $ret = "";
 my $sender = "your-mail-address\@example.com";
 my $konto = "your-mail-address\@example.com";  
 #account
 my $passwrd = "password";
 my $provider = "your.mail.server.address";
 Log 1, "sendEmail RCP: $rcpt";
 Log 1, "sendEmail Subject: $subject";
 Log 1, "sendEmail Text: $text";

 $ret .= qx(sendEmail -f '$sender' -t '$rcpt' -u '$subject' -m '$text' -s '$provider' -xu '$konto' -xp '$passwrd' -o tls=no);
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string
 Log 1, "sendEmail returned: $ret";
}

In fhem.cfg we add the function call

#########################################################
####   Battery low mail
#########################################################

define BatteryCheck_Notify notify .*:[Bb]attery.* { if( "$EVTPART1" !~ m/ok/ && "$EVTPART0" !~ m/runtime/ ) {\
 { DebianMail( 'your-mail-address@example.com', 'FHEM Battery warning', '$NAME $EVENT' ) };;\
  Log 3, "$NAME: Battery warning $EVENT";;\
 }\
}

which is triggered if a battery or Battery event that is not ok occurs. (This is specifically for the devices I have, others may require a different check.)
We can test this with

trigger MAX_075bd4 battery: low

from the command box (for a previously defined device such as my bathroom MAX! stat MAX_075bd4 listed above), check the Logfile for results or errors.

You could modify this to send a mail if myWeather indicates forecast is not fair

#########################################################
####   Poor weather mail
#########################################################

define PoorWeather_Notify notify .*:condition.* { if("$EVTPART1" !~ m/fair/) {\
 { DebianMail('your-mail-address@example.com', 'FHEM Poor Weather warning', '$NAME $EVENT')};;\
  Log 3, "$NAME: Poor Weather warning $EVENT";;\
 }\
}

This will generate a mail every time an event containing condition occurs that is not fair. (This will include for days ahead so expect a few - especially if in the UK!)



Controlling heating

This bit is more complex and involves a number of additions to both fhem.cfg and 99_myUtils.pm.
Its action is very crude and simple. One day I will change it :)
Let's start with a subroutine in 99_myUtils.pm

######## Valve position heating control  ############
sub
valve_pos($)
{
#Log 3, "Valve position dependent heating control...";
my $threshold_val = $_[0];
my $valve = 0;
my @pos = ();
my $total = 0;
my @MAX_HT=devspec2array("DEF=HeatingThermostat.*");
 foreach(@MAX_HT)
 {
  $valve=ReadingsVal( $_, "valveposition", "101" );
  push(@pos,$valve);
  $total = $total + $valve;
 }
 if ( ( $total > $threshold_val ) && (ReadingsVal( "Switch_01", "state", "off" ) eq "off") && (ReadingsVal( "Switch_02", "state", "off" ) eq "off"))
 {
  fhem "set Switch_01 on";
  fhem "define t0 at +00:00:15 { fhem \"set Switch_02 on\"}";
  Log 3, "Heating ON: @pos, Total: $total, Threshold: $threshold_val"
 }
 elsif ( ( $total < $threshold_val ) && (ReadingsVal( "Switch_01", "state", "off" ) eq "on" ) && (ReadingsVal( "Switch_02", "state", "off" ) eq "on"))
 {
  fhem "set Switch_02 off";
  fhem "define t1 at +00:00:30 { fhem \"set Switch_01 off\"}";
  Log 3, "Heating OFF: @pos, Total: $total, Threshold: $threshold_val"
 }
 else
 {
  Log 4, "Heating UNCHANGED: @pos, Total: $total, Threshold: $threshold_val"
 }
 fhem ("set Demand $total");
}

$threshold_val is passed in the subroutine call and is taken from a dummy defined in fhem.cfg whose value is set by hand from one of the setList values.

#########################################################
####   Heating control
#########################################################

define HeatingThreshold dummy
attr HeatingThreshold event-on-change-reading .*
attr HeatingThreshold room Heat_Control
attr HeatingThreshold setList state:30,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,90,100
attr HeatingThreshold webCmd state

define at_HeatingCheck at +*00:15:00 {valve_pos( ReadingsVal( "HeatingThreshold", "state", "50" ) )}
attr at_HeatingCheck room Heat_Control

An at is also included for regular checks in addition to a number of notify definitions in fhem.cfg eg.

#########################################################
####    Heating_Changes_notify change
#########################################################

define Heating_Changes_notify .*([Mm]ode|[Pp]ower[Oo]n|[Hh]eating[Tt]hreshold|[Vv]alve[Pp]osition).* { valve_pos( ReadingsVal( "HeatingThreshold", "state", "50" ) );
 my $valve = 0;
 my @pos = ();
 my $demand = 0;
 my @MAX_HT=devspec2array( "DEF=HeatingThermostat.*" );
 foreach(@MAX_HT)
 {
  $valve=ReadingsVal($_, "valveposition", "101");
  push(@pos,$valve);
  $demand=$demand+$valve;
 }
 fhem ("set Demand $demand");
}

which calls the subroutine everytime a change in valveposition occurs.

The code in 99_myUtils.pm totals the valveposition and if above $threshold_val then the pump is switched on and, following a short delay, the boiler too. Similar for switching them both off. The total of the valveposition is also set in another dummy in fhem.cfg

#########################################################
####   Demand
#########################################################

define Demand dummy
attr Demand room Heat_Control



Saving MAX thermostat configs

Another bit I have added following my Cube forgetting all devices!

It's just a quick hack to save data from all Heating Thermostats (in this post fhem user Wzut has suggested some useful modifications - not incorporated here).

#########################################################
#### get_valve_data
#########################################################

# run with something like
# {get_valve_data( )} # or # define at_get_valve_data at +00:01:00 { get_valve_data( ) }
# by default saves in /opt/fhem/FHEM as my_HT-<date>.cfg
#
# check the 'set' lines before using!
#
sub
get_valve_data( )
{
 my $msg;
 my $fname;
 my ( $sec,$min,$hour,$day,$month,$year,$wday,$yday,$isdst ) = localtime( time );
 $fname = sprintf( "%4d-%02d-%02d", $year + 1900, $month + 1, $day );
 my $hashes = "########################################\n";

 my $mpath = AttrVal( "global", "modpath", "." );
 my $ret = open( HTFILE, "> $mpath/FHEM/my_HT-$fname.cfg" );
 if( $ret )
 {
  my @MAX_HT=devspec2array( "DEF=HeatingThermostat.*" );
  foreach( @MAX_HT )
  {
   print HTFILE $hashes . $hashes;
   $msg = sprintf( "HeatingThermostat \t %s \t %s \n", $_, AttrVal( $_, "room", "" ) );
   print HTFILE $msg;
   print HTFILE $hashes . $hashes;
   print HTFILE "\n";

   # get basic settings
   $msg = sprintf( "boostDuration \t\t %2d \n", ReadingsVal( $_, "boostDuration", "0" ) );
   $msg .= sprintf( "boostValveposition \t %3d \n", ReadingsVal( $_, "boostValveposition", "101" ) );
   $msg .= sprintf( "comfortTemperature \t %2.1f \n", ReadingsVal( $_, "comfortTemperature", "0" ) );
   $msg .= sprintf( "decalcification \t %s \n", ReadingsVal( $_, "decalcification", "" ) );
   $msg .= sprintf( "ecoTemperature \t \t %2.1f \n", ReadingsVal( $_, "ecoTemperature", "0" ) );
   $msg .= sprintf( "maxValveSetting \t %3d \n", ReadingsVal( $_, "maxValveSetting", "101" ) );
   $msg .= sprintf( "maximumTemperature \t %s \n", ReadingsVal( $_, "maximumTemperature", "on" ) );
   $msg .= sprintf( "measurementOffset \t %2.1f \n", ReadingsVal( $_, "measurementOffset", "0" ) );
   $msg .= sprintf( "minimumTemperature \t %s \n", ReadingsVal( $_, "minimumTemperature", "on" ) );
   $msg .= sprintf( "valveOffset \t \t %2d \n", ReadingsVal( $_, "valveOffset", "0" ) );
   $msg .= sprintf( "windowOpenDuration \t %2d \n", ReadingsVal( $_, "windowOpenDuration", "0" ) );
   $msg .= sprintf( "windowOpenTemperature \t %2d \n", ReadingsVal( $_, "windowOpenTemperature", "0" ) );
   print HTFILE $msg . "\n";
   
   # get daily profiles
   $msg = parse_weekprofile( $_, "0-Sat" );
   print HTFILE "weekProfile for Saturday\n";
   print HTFILE $hashes;
   print HTFILE $msg;
   $msg = parse_weekprofile( $_, "1-Sun" );
   print HTFILE "weekProfile for Sunday\n";
   print HTFILE $hashes;
   print HTFILE $msg;
   $msg = parse_weekprofile( $_, "2-Mon" );
   print HTFILE "weekProfile for Monday\n";
   print HTFILE $hashes;
   print HTFILE $msg;
   $msg = parse_weekprofile( $_, "3-Tue" );
   print HTFILE "weekProfile for Tuesday\n";
   print HTFILE $hashes;
   print HTFILE $msg;
   $msg = parse_weekprofile( $_, "4-Wed" );
   print HTFILE "weekProfile for Wednesday\n";
   print HTFILE $hashes;
   print HTFILE $msg;
   $msg = parse_weekprofile( $_, "5-Thu" );
   print HTFILE "weekProfile for Thursday\n";
   print HTFILE $hashes;
   print HTFILE $msg;
   $msg = parse_weekprofile( $_, "6-Fri" );
   print HTFILE "weekProfile for Friday\n";
   print HTFILE $hashes;
   print HTFILE $msg;
   
   $msg = "\n\n";
   print HTFILE $msg;
  }
  close( HTFILE );
 }
 else
 {
  Log 1,"get_valve_data: Cannot open HT-$fname.dat for writing!";
 }
}
 
#########################################################
#### parse_weekprofile
#########################################################
sub
parse_weekprofile($$)
{
 my ( $ht, $dow ) = @_;
 my $msg;
 my $wp_msg;
 my @temps;
 my @times;
 @temps = split( ' /', ReadingsVal( $ht, "weekprofile-" . $dow . "-temp", "" ) );
 @times = split( ' /', ReadingsVal( $ht, "weekprofile-" . $dow . "-time", "" ) );
 for my $i ( 0 .. @temps - 1 )
 {
  my $ti;
  my $te;
  $te = $temps[$i];
  $te =~ s/\s*//;
  $te = substr( $te, 0, 4 );
  ( undef, $ti ) = split( '-', $times[$i] );
  $msg .= sprintf( "%2.1f \t %s \n", $te, $ti );
  $msg =~ s/\s*//;
  $msg =~ s/\x2E0//; # .0
 }
 $wp_msg = $msg;
 $wp_msg =~ s/\t/,/g;
 $wp_msg =~ s/\n/,/g;
 $wp_msg =~ s/\s//g;
 $wp_msg =~ s/,00:00,//;
 $msg .= "\n";
 $wp_msg = "set " . $ht . " weekProfile " . substr( $dow, -3, 3 ) . " " . $wp_msg;
 $msg .= $wp_msg . "\n\n";
 return $msg;
}

The saved file will be something similar to the following, with a section for each day.
The line
set MAX_0759c6 weekProfile Sat 15,02:00,15,04:00,16,06:00,16,08:00,16,10:00,16,12:00,16,14:00,16,16:00,16,18:00,16,20:00,16,22:00,15
is the command required to reset the thermostat's program for Saturday back to the saved settings.
Each thermostat will require a command for each day.

Note that reprogramming all thermostats may create a lot of radio traffic. Some of the commands may be queued and only sent after some delay (to keep within the 1% rule).

########################################
########################################
HeatingThermostat        MAX_0759c6      Hall 
########################################
########################################

boostDuration             5 
boostValveposition        80 
comfortTemperature       15.0 
decalcification          Sat 12:00 
ecoTemperature           12.0 
maxValveSetting          100 
maximumTemperature       on 
measurementOffset        0.0 
minimumTemperature       5.0 
valveOffset               0 
windowOpenDuration       15 
windowOpenTemperature    12 

weekProfile for Saturday
########################################
15       02:00 
15       04:00 
16       06:00 
16       08:00 
16       10:00 
16       12:00 
16       14:00 
16       16:00 
16       18:00 
16       20:00 
16       22:00 
15       00:00 

set MAX_0759c6 weekProfile Sat 15,02:00,15,04:00,16,06:00,16,08:00,16,10:00,16,12:00,16,14:00,16,16:00,16,18:00,16,20:00,16,22:00,15

weekProfile for Sunday
########################################
# etc.

The 98_weekprofile.pm module has recently been added to fhem (Jan 2016).
This module makes it much easier to manage weekprofiles for individual MAX (and HM) thermostats.

I considered altering the above script to extract data in a usable json format for this module but as one should only need to set a profile for each stat once
I think it is probably simpler to extract the string from the relevant fhem web page and reformat to the required json style in a text editor.

An example formatted profile is given at http://www.fhemwiki.de/wiki/Weekprofile - but note that it has to be transformed further as a single line as shown at the bottom of that page.



More complex stuff

rg_Heating readingsGroup config, see main page.

#########################################################
####   rg_Heating readingsGroup
#########################################################

define rg_Heating readingsGroup <%sani_heating>,<Open>,<Actual>,<Target>,<>,<>,<>,<Mode>,<Battery> \
<hr> \
NAME=MAX_07.*:valveposition,temperature,desiredTemperature,<{myUtils_HeatingUpDown($DEVICE,"up")}@desiredTemperature>,desiredNew,<{myUtils_HeatingUpDown($DEVICE,"down")}@desiredTemperature>,mode,battery

attr rg_Heating group rg1,&lt;br&gt;
attr rg_Heating style="color:black;;font-weight:bold"
attr rg_Heating room Heat_Control
attr rg_Heating valueFormat { temperature => "%.0f", desiredTemperature => "%.0f", valveposition =>"%.0f", maxValveSetting =>"%.0f"  }
attr rg_Heating valueIcon {'battery.ok' => 'batterie@green', 'battery.low' => 'batterie@red'}
attr rg_Heating valueStyle {($VALUE =~ m/00/)?'style="visibility:hidden"':($READING =~ m/valveposition/ && $NUM =~ m/0/)?'style="color:blue"':( $READING =~ m/valveposition/ && $NUM >= 0 )?'style="color:red;;font-weight:bold"':( $READING =~ m/temperature/ && $NUM > 23 )?'style="color:red"':'style="color:black"'}
attr rg_Heating valueSuffix { temperature => " °C", desiredTemperature => " °C", valveposition =>" %",maxValveSetting =>" %" }

Additional code has to be added to 99_myUtils.pm, this is included in the wiki so I haven't added it here.

(Updated replacing $VALUE with $NUM where necessary following 18/6/015 update).



rg_Indoors

readingsGroup config, see main page.

#########################################################
####   rg_Indoors readingsGroup
#########################################################

define rg_Indoors readingsGroup \
SWE3:<%temp_temperature>,,temperature \
SWE3:<%weather_humidity>,,humidity, \
SWE3:<%weather_humidity>,,absFeuchte \
SWE3:<%temperature_humidity>,,dewpoint
attr rg_Indoors group rg1
attr rg_Indoors room Heat_Control
attr rg_Indoors valueFormat { temperature => "%1.f", humidity => "%1.f", absFeuchte => "%1.f", dewpoint => "%1.f" } 
attr rg_Indoors valueStyle style="text-align:right"
attr rg_Indoors valueSuffix { \
if ( $READING =~ /temperature/ || $READING =~ /dewpoint/ ) { return " °C" } \
elsif ( $READING =~ /humidity/ || $READING =~ /absFeuchte/ ) { return " %" } \
}



rg_Weather readingsGroup config, see main page.

#########################################################
####    rg_Weather readingsGroup
#########################################################

define rg_Weather readingsGroup \
WeatherStation:<%temp_temperature>,<Temperature>,temperature \
WeatherStation:<%temp_temperature_min>,<Min-Temp>,temperature_min_day \
WeatherStation:<%temp_temperature_max>,<Max-Temp>,temperature_max_day \
WeatherStation:<%weather_humidity>,<Humidity>,humidity \
WeatherStation:<%temperature_humidity>,<Dewpoint>,dewpoint \
WindChill:<%temp_windchill>,<Windchill>,state \
WeatherStation:<%weather_wind_speed>,<WindSpeed>,windSpeed \
EnvSensor:<%weather_barometric_pressure>,<Pressure>,Pressure

attr rg_Weather group rg1
attr rg_Weather room Heat_Control,WeatherStation
attr rg_Weather valueFormat { temperature => '%1.f °C', temperature_min_day => '%1.f °C', temperature_max_day => '%1.f °C', humidity => '%1.f %%', dewpoint => '%1.f °C', state => '%1.f °C', windSpeed => '%1.f km/h', Pressure => '%s mb' }
attr rg_Weather valueStyle style="text-align:right"

I have since altered this slightly.

Instead of using a dummy to hold windchill along with a notify to calculate the value I have added a userReadings attribute to my weatherstation - and also added a text version of windDirection...

attr WeatherStation userReadings windDirectionText {Twilight_CompassPoint(ReadingsVal( "WeatherStation", "windDirection", 0) ) }, windChill { my $T=ReadingsVal( "WeatherStation", "temperature", 0 );;my $Vw=ReadingsVal "WeatherStation", "windSpeed", 0 );;my $TWC=0;;if ( $Vw > 5 ) { $TWC = 13.12 + 0.6215 * $T - 11.37 * ( $Vw ** 0.16 ) + 0.3965 * $T * ( $Vw ** 0.16 ) } else { $TWC = $T };;sprintf( '%.1f', $TWC ) }

so the line in the readingsGroup

WindChill:<%temp_windchill>,<Windchill>,state \

is changed to

WeatherStation:<%temp_windchill>,<Windchill>,windChill \

The valueFormat attribute will need modifying accordingly.



wl_CH_Counter

wl_CH_Counter config, see main page.

#########################################################
####   CH HourCounter
#########################################################

define Boiler_Counter HourCounter Boiler_Switch:on Boiler_Switch:off
attr Boiler_Counter room Heat_Control

define Pump_Counter HourCounter Pump_Switch:on Pump_Switch:off
attr Pump_Counter room Heat_Control

define Filelog_CH_Counter FileLog ./log/CH_Counter-%Y.log Boiler_Counter:.*|Pump_Counter:.*
attr Filelog_CH_Counter logtype text
attr Filelog_CH_Counter room Logs

define wl_CH_Counter SVG Filelog_CH_Counter:my_CH_Counter:CURRENT
attr wl_CH_Counter room Heat_Control
attr wl_CH_Counter title "Boiler, Pump switching"

and gplot file my_CH_Counter.gplot

set terminal png transparent size crop
set output '.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title ''
set ytics
set y2tics
set grid y2tics
set ylabel "Starts/OnFor (min)"
set y2label "min, hr, No."
set yrange [-20:]
set y2range [-15:30]
#set format y "%.1f"
#set format y2 "%.1f"
#================================================
#================================================
# if you need a ':' in regex or function replace it by '\x3a'
# syntax: ::: (@fld,0-based,NO space)
# :attr:0:[int|delta-h,delta-d|$fld[3]=~"on"?0.9:0.1]

#================================================
#================================================
#FileLog 4:Boiler_Counter.value\x3a:0:$fld[3]=~"1"?-2:-9
#FileLog 4:Pump_Counter.value\x3a:0:$fld[3]=~"1"?-12:-19
#FileLog 4:Boiler_Counter.countsPerDay\x3a:0:
#FileLog 4:Boiler_Counter.pulseTimePerDay\x3a:0:$fld[3]/=3600

#FileLog 4:Boiler_Counter.pulseTimeIncrement\x3a:0:$fld[3]/=60

#================================================
#================================================
# colors
# l0 ... red l0fill ... red l0dot ... red, dotted
# l1 ... green l1fill ... green l1dot ... green, dotted
# l2 ... blue l2fill ... blue l0fill_stripe ... red, filled, stripes
# l3 ... magenta l3fill ... magenta l1fill_stripe ... green, filled, stripes
# l4 ... brown l4fill ... yellow l0fill_gyr ... filled, multicolor
# l5 ... black l5fill ... cyan
# l6 ... olive l6fill ... black
# l7 ... gray
# l8 ... yellow
#================================================ # line types
# points
# lines
# steps - draws from the last to the new value with the old value a line
# fsteps - draws from the last to the new value with the new value line
# histeps - records the transition from the old to the new value centered between the values
# bars
#================================================
plot "<IN>" using 1:2 axes x1y1 title 'Boiler' ls l0 lw 1 with steps,\
     "<IN>" using 1:2 axes x1y1 title 'Pump' ls l4 lw 1 with steps,\
     "<IN>" using 1:2 axes x1y1 title 'Starts' ls l1 lw 1 with steps,\
     "<IN>" using 1:2 axes x1y2 title 'Hours' ls l2 lw 1 with steps,\
     "<IN>" using 1:2 axes x1y1 title 'Duration (min)' ls l3 lw 1 with fsteps
#================================================
#######################################################



myTest logProxy config, see main page.

#########################################################
####   myTest
#########################################################

define myTest SVG myProxy:my_logPolar:CURRENT
attr myTest label "Current state"
attr myTest plotsize 340,300
attr myTest room Test

and gplot file my_logPolar.gplot

set terminal png transparent size crop
set xlabel " "
set title ''
set title ''
set xtics ()
set ytics ()
set y2tics ()
set xrange [-40:40]
set yrange [-40:40]

#logProxy Polar::{[map{ReadingsVal($_,"temperature",0)}devspec2array("DEF=HeatingThermostat.*")]}
#logProxy Polar::{[map{ReadingsVal($_,"desiredTemperature",0)}devspec2array("DEF=HeatingThermostat.*")]}
#logProxy Polar::{[map{ReadingsVal($_,"valveposition",0)}devspec2array("DEF=HeatingThermostat.*")]}
#logProxy Polar::{[map{ReadingsVal($_,"temperature",0)}devspec2array("DEF=HeatingThermostat.*")]}
#logProxy Polar::{[devspec2array("DEF=HeatingThermostat.*")]}

plot "<IN>" using 1:2 axes x1y1 title 'Current' ls l0 lw 1 with lines,\
     "<IN>"using 1:2 axes x1y1 title 'Desired' ls l1fill lw 1 with lines,\
     "<IN>" using 1:2 axes x1y1 title 'Valve%' ls l5 lw 1 with lines,\
     "<IN>" using 1:2 axes x1y1 notitle ls l0 lw 1 with points,\
     "<IN>" using 1:2 axes x1y1 notitle ls l2 lw 1 with lines



wl_Bath_Room logProxy config, see main page.
#########################################################
####   wl_Bath_Room
#########################################################

define wl_Bath_Room SVG myProxy:my_logP_HT:CURRENT
attr wl_Bath_Room label "Bathroom temperature: Min: $data{min5}, Max: $data{max5}, Last: $data{currval5}, Curr: $data{currval6} °C, Valve: $data{currval7} %"
attr wl_Bath_Room plotfunction WeatherStation MAX_075bd4
attr wl_Bath_Room plotsize 800,200
attr wl_Bath_Room room Bathroom,Test

and gplot file my_logP_HT.gplot

set terminal png transparent size crop
set output '.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title ''
set ytics
set y2tics
set grid y2tics
set ylabel "percent %"
set y2label "Temperature (deg C)"
set yrange [0:100]
set y2range [:25]

#FileLog 4::temperature::
#FileLog 4::humidity::
#FileLog 4::dewpoint::
#FileLog 4::windSpeed::delta-h

#FileLog 4::desiredTemperature:4.5:
#FileLog 4::temperature:4.5:
#FileLog 4::valveposition:0:

#logProxy FileLog:FileLog_:4:.temperature::
#logProxy FileLog:FileLog_:4:.humidity::
#logProxy FileLog:FileLog_:4:.dewpoint::
#logProxy FileLog:FileLog_:4:.windSpeed::delta-h

#logProxy ConstY:0

#logProxy FileLog:FileLog_:4:.desiredTemperature::
#logProxy FileLog:FileLog_:4:.temperature::
#logProxy FileLog:FileLog_:4:.valveposition::

#logProxy Func:logProxy_WeekProfile2Plot("",$from,$to)

plot "<IN>" axes x1y2 title 'Ext-temp' ls l0 lw 1 with lines, \
     "<IN>" axes x1y1 title 'humidity' ls l1 lw 1 with lines,\
     "<IN>" axes x1y2 title 'dewpoint' ls l2 lw 1 with lines,\
     "<IN>" axes x1y1 title 'windspeed' ls l6fill lw 1 with steps,\
     "<IN>" using 1:2 axes x1y2 notitle ls l4 lw 1 with steps,\
     "<IN>" axes x1y2 title 'desiredTemp' ls l3 lw 1 with steps,\
     "<IN>" axes x1y2 title 'Int-temp' ls l1fill lw 1 with lines,\
     "<IN>" axes x1y1 title 'valve %' ls l5 lw 1 with histeps,\
     "<IN>" using 1:2 axes x1y2 title 'weekprofile' ls l7 lw 1 with steps
###



Waste collection visualisation

Another readingsGroup config (for dustbins and recycling), see main page.

Firstly we need a holiday file containing the dates of the various collections.
Before adding the following code to 99_myUtils.pm you must add

use Date::Calc qw( Delta_Days Add_Delta_Days Day_of_Week Day_of_Week_Abbreviation Nth_Weekday_of_Month_Year );

to the beginning.
Running

{waste_calendar(20150115,14,"Grey_bin")}

(where 20150115 (15th Jan 2015) is the first date of collection, 14 is the interval between collection in days and Grey_bin is the name for that collection.)
which will create a file with a name like wastecalendar-Grey_bin.holiday. The contents of created files should be copied into a file wastecalendar.holiday (no need to sort)

The code checks for a holiday Monday in the same week and if so adds a day to the collection date - this won't work in all cases! Collection agencies will also have their own ways of dealing with holidays. Any wrong dates can be manually edited in the holiday file.

You can also adjust to output type 1 format should you prefer rather than type 3 as written.

Updated to cope with a holiday modified collection running into the following month. (Jan 2015)

#########################################################
#### waste_calendar
#########################################################
# create file for waste collection
# call with
# {waste_calendar(20160114,14,"Grey_bin")}
#
# to generate file with start 14th Jan 2016, interval 14 days, event name Grey_bin
# checks for holiday Mondays and adds a day - beware Dec and Jan! Not predictable here
#
# {waste_calendar(20160107,14,"Blue_bin")}
# {waste_calendar(20160107,14,"Brown_bin")}
#
# for use see
# http://forum.fhem.de/index.php/topic,32382.msg254506.html#msg254506
#########################################################

waste_calendar( $$$ )
{
 my ( $date_string, $interval, $event ) = @_;
 my $msg;
 my $msg2;
 my $nth;
 my $type = 3; # holiday file type - see commandref
 my $type2 = 1; # in case type 1 preferred
 my $hname = "en"; # holiday filename - command is 'get en MM-DD'
 
 if ( $date_string !~ /(\d{4})(\d\d)(\d\d)/ ) # extract date
 {
  print "Bad data string provided: $date_string\n";
  return 0;
 }
 my ($start_year, $month, $day) = ($1, $2, $3);
 my $year = $start_year;
 my $mpath   = AttrVal( "global", "modpath", "." );
 my $ret  = open( WCFILE, "> $mpath/FHEM/wastecalendar-$event.holiday" );
 if( $ret )
 {
  $msg = sprintf( "##############################\n" );
  $msg .= sprintf( "# Waste calendar for year %4d\n", $start_year );
  $msg .= sprintf( "# %s\n", $event );
  $msg .= sprintf( "##############################\n" );
  print WCFILE $msg;
  $msg = sprintf( "# First date $event %4d %02d %02d\n", $start_year, $month, $day );
  print WCFILE $msg;
  do 
  {
   my $dow = Day_of_Week( $year, $month, $day ); # check for Bank Holiday Monday (Christmas, New Year too variable here to guess)
   my ( $tmpyear, $tmpmonth, $tmpday ); # previous Monday
   ( $tmpyear, $tmpmonth, $tmpday ) = Add_Delta_Days( ( $year, $month, $day ) , -$dow + 1 );
   my $cmd = sprintf( "get $hname MM-DD %02d-%02d", $tmpmonth, $tmpday ); # check holiday Monday
   my $rslt = fhem( $cmd, 1 ); # added ,1 here to reduce logging?
   if ( $rslt ne "none" )
   {
    $dow++; # add a day
    # corrected to roll over month!
    ( $tmpyear, $tmpmonth, $tmpday ) =  Add_Delta_Days( $year, $month, $day, 1 ); 
    $nth = int( Nth_Weekday_of_Month_Year( $tmpyear, $tmpmonth, $dow, 1 ) );
   }
   else
   {
    $nth = int( Delta_Days( Nth_Weekday_of_Month_Year( $year, $month, $dow, 1 ), $year, $month, $day ) / 7 ) + 1; # calc nth
   }
   $msg = sprintf( "%d ", $type ); # holiday type
   $msg .= sprintf( "%d ", $nth ); # nth weekday in month
   $msg .= sprintf( "%s " , Day_of_Week_Abbreviation( $dow ) ); # short letter day
   if ( $month == $tmpmonth )
   {
     $msg .= sprintf( "%02d ", $month ); # month no.
   }
   else
   {
     # if month has rolled over for holiday Monday only
     $msg .= sprintf( "%02d ", $tmpmonth );
   }
   $msg .= sprintf( "%s\n", $event ); # event name
   print WCFILE $msg; # disable for type 1 format
   $msg2 = sprintf( "# %d ", $type2 );
   $msg2 .= sprintf( "%02d-%02d ", $month, $day );
   $msg2 .= sprintf( "%s\n", $event );
   # print WCFILE $msg2; # alternate type 1 event
   ( $year, $month, $day ) = Add_Delta_Days( ( $year, $month, $day ) , $interval );
  }
  while ( $year < $start_year + 1 );
  close( WCFILE );
 }
 else
 {
  Log 1,"waste_calendar: Cannot open wastecalendar-$event.holiday for writing!";
 }
}
#########################################################
#### END waste_calendar
#########################################################

The relevant bits for fhem.cfg are (enter from command line not directly into the file)

#########################################################
####   Waste collection
#########################################################

define rgWasteCalendar readingsGroup wastecalendar:!title \
wastecalendar:!day|0,!Grey_bin|0,!Blue_bin|0,!Brown_bin|0 \
wastecalendar:!day|1,!Grey_bin|1,!Blue_bin|1,!Brown_bin|1 \
wastecalendar:!day|2,!Grey_bin|2,!Blue_bin|2,!Brown_bin|2 \
wastecalendar:!day|3,!Grey_bin|3,!Blue_bin|3,!Brown_bin|3 \
wastecalendar:!day|4,!Grey_bin|4,!Blue_bin|4,!Brown_bin|4 \
wastecalendar:!day|5,!Grey_bin|5,!Blue_bin|5,!Brown_bin|5 \
wastecalendar:!day|6,!Grey_bin|6,!Blue_bin|6,!Brown_bin|6 \
wastecalendar:!day|7,!Grey_bin|7,!Blue_bin|7,!Brown_bin|7 \
wastecalendar:!day|8,!Grey_bin|8,!Blue_bin|8,!Brown_bin|8 \
wastecalendar:!day|9,!Grey_bin|9,!Blue_bin|9,!Brown_bin|9 \
wastecalendar:!day|10,!Grey_bin|10,!Blue_bin|10,!Brown_bin|10 \
wastecalendar:!day|11,!Grey_bin|11,!Blue_bin|11,!Brown_bin|11 \
wastecalendar:!day|12,!Grey_bin|12,!Blue_bin|12,!Brown_bin|12 \
wastecalendar:!day|13,!Grey_bin|13,!Blue_bin|13,!Brown_bin|13 \
wastecalendar:!day|14,!Grey_bin|14,!Blue_bin|14,!Brown_bin|14
attr rgWasteCalendar cellStyle { "r:1"=>'style="font-weight:bold;;font-size:16px"',"c:1"=>'style="font-weight:bold;;text- align:right;;padding-left:28pt;;"'}
attr rgWasteCalendar group rg2
attr rgWasteCalendar nonames 1
attr rgWasteCalendar room Bins
attr rgWasteCalendar valueColumns { title => 'colspan="7"' }
attr rgWasteCalendar valueFormat {if($READING eq 'title'){$VALUE="recycling"}else{my($r,$d)=split(/\|/,$READING);;my $v=fhem("get wastecalendar days $d",1);;if($v eq "none"){$VALUE=undef}else{if($r eq 'day'){if($d==0){$VALUE="Today"}elsif($d==1){$VALUE="Tomorrow"}else{$VALUE="In $d Days"}}else{if($v=~m/$r/){$VALUE=1}else{$VALUE=' '}}}}}
attr rgWasteCalendar valueIcon {if($VALUE eq 'recycling'){$VALUE}elsif($VALUE eq 1){if($READING=~m/Grey_bin/){$VALUE='dustbin@5C5959'}elsif($READING=~m/Blue_bin/){$VALUE='dustbin@0617EE'} elsif($READING=~m/Brown_bin/){$VALUE='dustbin@E86915'}}else{$VALUE=''}}
attr rgWasteCalendar valueSuffix {'title'=>' Recycling'}
attr rgWasteCalendar verbose 0

define wastecalendar holiday
attr wastecalendar room Bins



Weather history

See main page.
First of all I have a number of definitions set up using the average module

#########################################################
#### average
#########################################################

define Wind_Avg average WeatherStation:windSpeed.*
attr Wind_Avg room WeatherStation

define Temp_Avg average WeatherStation:temperature.*
attr Temp_Avg room WeatherStation

define Rain_Avg average WeatherStation:rain.*
attr Rain_Avg room WeatherStation

define rHum_Avg average WeatherStation:humidity.*
attr rHum_Avg room WeatherStation

define aHum_Avg average WeatherStation:absFeuchte.*
attr aHum_Avg room WeatherStation

I have a separate log for this data alongside monthly and yearly logs for the WeatherStation...

#########################################################
#### hm weatherstation logs
#########################################################

define FileLog_Weather_Year FileLog ./log/Weather_Year-%Y.log WeatherStation:T:.*
attr FileLog_Weather_Year logtype text
attr FileLog_Weather_Year room Logs

define FileLog_avgLog FileLog ./log/average-%Y.log WeatherStation:.*(_day|_month).*
attr FileLog_avgLog archivedir /opt/fhem/log-archive/
attr FileLog_avgLog logtype text
attr FileLog_avgLog nrarchive 2
attr FileLog_avgLog room Logs

The second logs all values that contain the text '_day' or '_month'.

Now we need some gplot file definitions...
NB. the next block contains the contents of 4 separate files with my usual comment lines removed.

#########################################################
#### my_avgHum.gplot
#########################################################
# Created by FHEM/98_SVG.pm, 2013-12-24 08:04:50
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title '<L1>'
set ytics
set y2tics
set grid y2tics
set ylabel "rHumidity %"
set y2label "rHumidity %"
set yrange [0:100]
set y2range [0:100]

#FileLog 4:WeatherStation.humidity_avg_day\x3a:0:

plot "<IN>" using 1:2 axes x1y1 title 'rHumidity avg' ls l1fill lw 0.5 with steps
#########################################################

#########################################################
#### my_avgRain.gplot
#########################################################
# Created by FHEM/98_SVG.pm, 2013-12-24 08:25:24
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title '<L1>'
set ytics
set y2tics
set grid y2tics
set ylabel "Rain (mm)"
set y2label "Rain (mm)"
set yrange [0:]
set y2range [0:]

#FileLog 4:WeatherStation.rain_max_day\x3a:0:delta-d

plot "<IN>" using 1:2 axes x1y2 title 'Rain' ls l2fill lw 1 with steps
#########################################################


#########################################################
#### my_avgTemp.gplot
#########################################################
# Created by FHEM/98_SVG.pm, 2013-12-24 08:04:50
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title '<L1>'
set ytics
set y2tics
set grid y2tics
set ylabel "Temperature (deg C)"
set y2label "Temperature (deg C)"
set yrange []
set y2range []

#FileLog 4:WeatherStation.temperature_max_day\x3a:0:
#FileLog 4:WeatherStation.temperature_avg_day\x3a:0:
#FileLog 4:WeatherStation.temperature_min_day\x3a:0:

plot "<IN>" using 1:2 axes x1y2 title 'Temperature max' ls l0fill lw 1 with steps,\
     "<IN>" using 1:2 axes x1y2 title 'Temperature avg' ls l5 lw 1 with lines,\
     "<IN>" using 1:2 axes x1y2 title 'Temperature min' ls l2fill lw 1 with steps
#########################################################

#########################################################
#### my_avgWind.gplot
#########################################################
# Created by FHEM/98_SVG.pm, 2013-12-24 08:25:24
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title '<L1>'
set ytics
set y2tics
set grid y2tics
set ylabel "WindSpeed (km/h)"
set y2label "WindSpeed (km/h)"
set yrange [0:]
set y2range [0:]

#FileLog 4:WeatherStation.windSpeed_max_day\x3a:0:
#FileLog 4:WeatherStation.windSpeed_avg_day\x3a:0:

plot "<IN>" using 1:2 axes x1y1 title 'WindSpeed max' ls l3fill lw 1 with steps,\
     "<IN>" using 1:2 axes x1y1 title 'WindSpeed avg' ls l5 lw 1 with lines
#########################################################

Now for the actual plot definitions...

define wl_month_weather_avgHum SVG FileLog_avgLog:my_avgHum:CURRENT
attr wl_month_weather_avgHum fixedrange 28days
attr wl_month_weather_avgHum label "Last28 days - Humidity: Min: $data{min1}, Max: $data{max1} %rH"

define wl_month_weather_avgRain SVG FileLog_avgLog:my_avgRain:CURRENT
attr wl_month_weather_avgRain fixedrange 28days
attr wl_month_weather_avgRain label "Last 28 days - Rain: Max: $data{max1 }mm"

define wl_year_weather_avgTemp SVG FileLog_avgLog:my_avgTemp:CURRENT
attr wl_year_weather_avgTemp fixedrange year
attr wl_year_weather_avgTemp label "This year - Temperature: Min: $data{min3}, Max: $data{max1}° C"

define wl_month_weather_avgWind SVG FileLog_avgLog:my_avgWind:CURRENT
attr wl_month_weather_avgWind fixedrange 28days
attr wl_month_weather_avgWind label "Last 28 days - WindSpeed: Max: $data{max1}km/h"

define wl_year_weather_avgHum SVG FileLog_avgLog:my_avgHum:CURRENT
attr wl_year_weather_avgHum fixedrange year
attr wl_year_weather_avgHum label "This year - relHumidity: Min: $data{min1}, Max: $data{max1}%"

define wl_year_weather_avgRain SVG FileLog_avgLog:my_avgRain:CURRENT
attr wl_year_weather_avgRain fixedrange year
attr wl_year_weather_avgRain label "This year - Rain: Max: $data{max1}mm"

define wl_month_weather_avgTemp SVG FileLog_avgLog:my_avgTemp:CURRENT
attr wl_month_weather_avgTemp fixedrange 28days
attr wl_month_weather_avgTemp label "Last 28 days - Temperature: Min: $data{min3}, Max: $data{max1} °C"

define wl_year_weather_avgWind SVG FileLog_avgLog:my_avgWind:CURRENT
attr wl_year_weather_avgWind fixedrange year
attr wl_year_weather_avgWind label "This year - WindSpeed: Max: $data{max1}km/h"



rg_Weather revisited

See main page.
First of all this requires the myDiff subroutine to be added to 99_myUtils.pm , this is available from the fhem wiki HM Weatherstation page.

This subroutine calculates the difference in values from the last relevant line of a LogFile to another calculated by from a given difference in time.

We already have rainfall today in the WeatherStation rain_calc_d_curr value, which is provided by the rain module - it gives rainfall to/from 07:30 localtime by default.
We need to create two dummies to hold the values for 28 days and year to date...

#########################################################
#### rain history
#########################################################

define RainLast28Days dummy
attr RainLast28Days room WeatherStation

define RainThisYear dummy
attr RainThisYear room WeatherStation

Now we have to calculate the total rainfall values to fill these, we'll use the at here, it makes sense to run slightly later than when the average module does its stuff.

My log looks something like this...

2015-01-01_00:00:43 WeatherStation rain_max_day: 955.8
2015-01-01_00:00:43 WeatherStation rain_min_day: 955.5
2015-01-01_00:00:43 WeatherStation rain_avg_month: 911.5
2015-01-01_00:00:43 WeatherStation rain_max_month: 955.8
2015-01-01_00:00:43 WeatherStation rain_min_month: 881.8
2015-01-01_00:00:43 WeatherStation temperature_avg_day: 2.9
2015-01-01_00:00:43 WeatherStation temperature_max_day: 7.1
2015-01-01_00:00:43 WeatherStation temperature_min_day: -1.5
2015-01-01_00:00:43 WeatherStation temperature_avg_month: 5.3
2015-01-01_00:00:43 WeatherStation temperature_max_month: 13.8
2015-01-01_00:00:43 WeatherStation temperature_min_month: -5.1
2015-01-01_00:00:43 WeatherStation windSpeed_avg_day: 3.7
2015-01-01_00:00:43 WeatherStation windSpeed_max_day: 14.5
2015-01-01_00:00:43 WeatherStation windSpeed_min_day: 0.0
2015-01-01_00:00:43 WeatherStation windSpeed_avg_month: 9.0
2015-01-01_00:00:43 WeatherStation windSpeed_max_month: 38.1
2015-01-01_00:00:43 WeatherStation windSpeed_min_month: 0.0
2015-01-01_00:00:43 WeatherStation humidity_avg_day: 88.7
2015-01-01_00:00:43 WeatherStation humidity_max_day: 95.0
2015-01-01_00:00:43 WeatherStation humidity_min_day: 77.0
2015-01-01_00:00:43 WeatherStation humidity_avg_month: 85.3
2015-01-01_00:00:43 WeatherStation humidity_max_month: 95.0 
2015-01-01_00:00:43 WeatherStation humidity_min_month: 60.0
2015-01-02_00:00:11 WeatherStation rain_max_day: 956.7
2015-01-02_00:00:11 WeatherStation rain_min_day: 955.8
2015-01-02_00:00:11 WeatherStation temperature_avg_day: 8.8
2015-01-02_00:00:11 WeatherStation temperature_max_day: 12.8

we are only interested in the values in the highlighted lines so our call to myDiff has to reflect this...

define at_Rain28Days at *00:30 {
 my $last28days = myDiff("2419200", "FileLog_avgLog", "4:WeatherStation.rain_max_day::");
 fhem("set RainLast28Days $last28days");
}
attr at_Rain28Days room WeatherStation

define at_RainThisYear at *00:35 {
 my ( $sec, $min, $hour, $day, $month, $year, $wday, $yday, $isdst ) = localtime;
 my @start = ( $year, 1, 1 );
 my @end = ( $year, $month + 1, $day );
 my $diff_days = Delta_Days( @start, @end );
 my $rainthisyear = myDiff( $diff_days * 86400, "FileLog_avgLog", "4:WeatherStation.rain_max_day::" );
 fhem("set RainThisYear $rainthisyear");
}
attr at_RainThisYear room WeatherStation

The first uses the number of seconds in 28 days - 2419200.
The second calculates the number of days since 1st January in the current year, multiplies this by the number of seconds in a day and uses this figure.

We now have to alter the rg_Weather definition slightly to look like this..

define rg_Weather readingsGroup \
WeatherStation:<%temp_outside>,<Temperature>,temperature \
WeatherStation:<%temp_temperature_max>,<Max&nbsp;temp>,temperature_max_day \
WeatherStation:<%temp_temperature_min>,<Min&nbsp;temp>,temperature_min_day \
WeatherStation:<%weather_humidity>,<Humidity>,humidity \
WeatherStation:<%temperature_humidity>,<Dewpoint>,dewpoint \
WeatherStation:<%temp_windchill>,<Windchill>,windChill \
WeatherStation:<%weather_wind_speed>,<Wind&nbsp;speed>,windSpeed \
EnvSensor:<%weather_barometric_pressure>,<Pressure>,Pressure \
WeatherStation:<%weather_rain_gauge>,<Today>,rain_calc_d_curr \
WeatherStation:<%weather_rain>,<isRaining>,isRaining\
RainLast28Days:<%weather_rain_gauge>,<28&nbsp;days>,state \
RainThisYear:<%weather_rain_gauge>,<This&nbsp;year>,state
myTwilight:<%weather_sunrise>,<Sunrise>,sr
myTwilight:<%weather_sunset>,<Sunset>,ss
attr rg_Weather group rg2
attr rg_Weather room Heat_Control,WeatherStation
attr rg_Weather valueFormat { temperature => "%.1f", temperature_max_day => "%.1f", temperature_min_day => "%.1f", humidity => "%1.f", dewpoint => "%.1f", windChill => "%.1f", windSpeed => "%1.f", Pressure => "%s", rain_calc_d_curr => "%.1f", 'RainLast28Days.state' => "%1.f", 'RainThisYear.state' => "%1.f" }
attr rg_Weather valueIcon {'isRaining.0' => 'weather_cloudy_light', 'isRaining.1' => 'weather_rain@blue'}
attr rg_Weather valueStyle style="text-align:right"
attr rg_Weather valueSuffix { \
if ( $READING =~ /temperature/ || $READING =~ /dewpoint/ || $READING =~ /windChill/ ) { return " °C" }\
elsif ( $READING =~ /humidity/ || $READING =~ /absFeuchte/ ) { return " %" }\
elsif ( $READING =~ /windSpeed/ ) { return " km/h" }\
elsif ( $READING =~ /Pressure/ ) { return " mb" }\
elsif ( $READING =~ /rain_/ ) { return " mm" }\
elsif ( InternalVal( $DEVICE, 'TYPE', ' ' ) eq 'dummy' ) { return " mm" }\
}

Note the use of the &nbsp; html entity to replace the space in split words in the definition, we also need to qualify the 'state' values in the valueFormat line, both highlighted above - otherwise they all end up with the same formatting!

I have added the attribute valueSuffix to some readingsGRoup examples. The match expressions in valueSuffix could, of course, be rewritten as

attr rg_Weather valueSuffix { \           
if ( $READING =~ /temperature|dewpoint|windChill/ ) { return " °C" }\
elsif ( $READING =~ /humidity|absFeuchte/ ) { return " %" }\
elsif ( $READING =~ /windSpeed/ ) { return " km/h" }\
elsif ( $READING =~ /Pressure/ ) { return " mb" }\
elsif ( $READING =~ /rain_/ ) { return " mm" }\
elsif ( InternalVal( $DEVICE, 'TYPE', ' ' ) eq 'dummy' ) { return " mm" }\
}

and more...

I thought it would be interesting to show atmospheric pressure trends so spent a while looking for a simple solution. My environment sensor (Umweltsensor) gives me a value for atmospheric pressure, the statistics module has the facility to show trending.

The definition of my environment sensor is...

define EnvSensor OWDevice 20.25C007000000 300
attr EnvSensor IODev myLocalOWServer
attr EnvSensor model DS2450
attr EnvSensor room OWDevice
attr EnvSensor stateFormat { sprintf( "SolarEnergy: %.1f Sun: %.0f Brightness: %.1f Pressure: %d", ReadingsVal( "EnvSensor", "SolarEnergy", 0 ), ReadingsVal( "EnvSensor", "Sun", 0 ), ReadingsVal( "EnvSensor", "Brightness", 0 ), ReadingsVal( "EnvSensor", "Pressure", 0 ) ) }
attr EnvSensor userReadings SolarEnergy { sprintf( "%d", ReadingsVal( "EnvSensor", "volt.C", 0 ) / 0.0038073 ) }, Sun { sprintf( "%0.2f", ReadingsVal( "EnvSensor", "volt.C", 0 ) ) }, Brightness { sprintf( "%0.2f", ReadingsVal( "EnvSensor", "volt.B", 0 ) ) }, Pressure { sprintf( "%d", ( ReadingsVal( "EnvSensor", "volt.D", 0 ) / ReadingsVal( "EnvSensor", "volt.A", 0 ) - ( 0 * 0.001059 ) + 0.1518 )  / 0.001059 ) }

I have added another definition for the statistics module which ignores all but my Pressure Reading and calculates trending for 1, 2, 3 and 6 hours.
The singularReadings creates individual Readings and values within EnvSensor.

define EnvSensorStats statistics EnvSensor
attr EnvSensorStats excludedReadings EnvSensor:PIO.*|EnvSensor:SolarEnergy|EnvSensor:Sun|EnvSensor:volt.*|EnvSensor:Brightness
attr EnvSensorStats room OWDevice
attr EnvSensorStats singularReadings EnvSensor:Pressure:Tendency:(1h|2h|3h|6h)
attr EnvSensorStats tendencyReadings Pressure

I have changed the valueStyle attribute for Pressure in readingsGroup rg_weather so the relevant code is now...
(for anyone wondering, this multi-line format is fine)

attr rg_Weather valueStyle {
if( $READING eq "rain_calc_d_curr" && $NUM > 0 || ( ( $READING =~ m/temperature/ || $READING eq "windChill" ) && $NUM <= 0 ) ){ 'style="color:blue;text-align:right;font-weight:bold"' }\
elsif( $READING =~ m/temperature/ && $NUM >= 20 ){ 'style="color:red;text-align:right;font-weight:bold"' }
elsif( $READING =~ m/Pressure/ && ReadingsVal( $DEVICE, "statPressureTendency3h", "" ) =~ m/\+0|\-0/ ){ 'style="color:black;text-align:right"' }
elsif( $READING =~ m/Pressure/ && ReadingsVal( $DEVICE, "statPressureTendency3h", "" ) =~ m/\+/ ){ 'style="color:red;text-align:right"' }
elsif( $READING =~ m/Pressure/ && ReadingsVal( $DEVICE, "statPressureTendency3h", "" ) =~ m/\-/ ){ 'style="color:blue;text-align:right"' }
else{ 'style="color:black;text-align:right"' } 
}

and shows rising pressure in red, falling in blue. The trend is the change over a three hour period and the display could be further enhanced using the following values as a guide...

  • Rising/falling more slowly - rising/falling at a progressively slower rate through the preceding 3 hours
  • Rising/falling slowly - change of 0.1 to 1.5mb in the preceding 3 hours
  • Rising/falling - change of 1.6 to 3.5mb in the preceding 3 hours
  • Rising/falling quickly - change of 3.6 to 6.0mb in preceding 3 hours
  • Rising/falling very rapidly - change greater than 6.0mb in the preceding 3 hours
  • Now rising/falling - Pressure has been rising/falling or steady in the preceding 3 hours, but at time of observation was definitely rising/falling


RoomInfo

See main page.

Another readingsGroup that shows values from my panStamp temperature/humidity sensors in tabular form.
(Apologies, this one seems to have turned into variations on a theme!)

It uses the fairly new but as yet undocumented calc fields $max and $min.

define RoomInfo readingsGroup <%status_comfort>,<Temperature>,<|>,<Max temp>,<|>,<Min temp>,<|>,<rHumidity>,<|>,<Max hum>,<|>,<Min hum>,<|>,<aHumidity>,<|>,<Dewpoint> \
SWAP.(0A|0B|0C|0D|0E):,temperature,<|>,$max(1..$ROW:1),<|>,$min(1..$ROW:1),<|>,humidity,<|>,$max(1..$ROW:7),<|>,$min(1..$ROW:7),<|>,absFeuchte,<|>,dewpoint

attr RoomInfo mapping %ROOM
attr RoomInfo nameStyle style="color:black;;font-weight:bold"
attr RoomInfo valueFormat { temperature => "%.1f °C", max => "%.1f °C", min => "%.1f °C", humidity => "%.1f %%", max => "%.1f %%", humidity => "%.1f %%", min => "%.1f %%", absFeuchte => "%.1f %%", dewpoint => "%.1f °C" }
attr RoomInfo valueStyle { if($READING eq "temperature" && $VALUE > 23){ 'style="color:red;;font-weight:bold;;text-align:center"' }elsif( $READING eq "temperature" && $VALUE <= 20 ){ 'style="color:blue;;text-align:center"' }elsif( $READING eq "humidity" && $VALUE > 65 ){ 'style="color:red;;font-weight:bold;;text-align:center"' }elsif( $READING eq "humidity" && $VALUE < 60 ){ 'style="color:blue;;text-align:center"' }else{ 'style="color:black;;text-align:center"' } }

Note that the column values used in the $max and $min entries (highlighted) must include the separator fields in the count if used.
Note also my comments regarding the valueFormat of the max and min fields on the main page in my original version.

This version corrects the formatting using aliases as highlighted...

define RoomInfo readingsGroup <%status_comfort>,<Temperature>,<|>,<Max temp>,<|>,<Min temp>,<|>,<rHumidity>,<|>,<Max hum>,<|>,<Min hum>,<|>,<aHumidity>,<|>,<Dewpoint> \
SWAP.(0A|0B|0C|0D|0E):,temperature,<|>,$max(1..$ROW:1)@t1,<|>,$min(1..$ROW:1)@t2,<|>,humidity,<|>,$max(1..$ROW:7)@h1,<|>,$min(1..$ROW:7)@h2,<|>,absFeuchte,<|>,dewpoint

attr RoomInfo mapping %ROOM
attr RoomInfo nameStyle style="color:black;;font-weight:bold"
attr RoomInfo valueFormat { temperature => "%.1f°C", t1 => "%.1f°C", t2 => "%.1f°C", humidity => "%.1f %%", h1 => "%.1f %%", h2 => "%.1f %%", absFeuchte => "%.1f %%", dewpoint => "%.1f°C" }
attr RoomInfo valueStyle { if($READING eq "temperature" && $VALUE > 23){ 'style="color:red;;font-weight:bold;;text-align:center"' }elsif( $READING eq "temperature" && $VALUE <= 20 ){ 'style="color:blue;;text-align:center"' }elsif( $READING eq "humidity" && $VALUE > 65 ){ 'style="color:red;;font-weight:bold;;text-align:center"' }elsif( $READING eq "humidity" && $VALUE < 60 ){ 'style="color:blue;;text-align:center"' }else{ 'style="color:black;;text-align:center"' } }

Finally a version that does exactly what I want but first needs the average module thus...

define MinMax average SWAP.*:( temperature|humidity ).*

The statistics module provides similar data but Min, Max and Avg are combined within a single reading so not as easy to manipulate.

define RoomInfo readingsGroup <%status_comfort>,<Temperature>,<|>,<Max temp>,<|>,<Min temp>,<|>,<rHumidity>,<|>,<Max hum>,<|>,<Min hum>,<|>,<aHumidity>,<|>,<Dewpoint> \
SWAP.(0A|0B|0C|0D|0E):,temperature,<|>,temperature_max_day,<|>,temperature_min_day,<|>,humidity,<|>,humidity_max_day,<|>,humidity_min_day,<|>,absFeuchte,<|>,dewpoint

attr RoomInfo mapping %ROOM
attr RoomInfo nameStyle style="color:black;;font-weight:bold"
attr RoomInfo valueFormat { temperature => "%.1f", temperature_max_day => "%.1f", temperature_min_day => "%.1f", humidity => "%.1f", humidity_max_day => "%.1f", humidity_min_day => "%.1f", absFeuchte => "%.1f", dewpoint => "%.1f", 'timestamp' => ReadingsTimestamp($DEVICE,"temperature","") }
attr RoomInfo valueStyle { if( $READING =~ m/temperature/ && $VALUE > 23 ){ 'style="color:red;;font-weight:bold;;text-align:center"' }elsif( $READING =~ m/temperature/ && $VALUE <= 20 ){ 'style="color:blue;;text-align:center"' }elsif( $READING =~ m/humidity/ && $VALUE > 65 ){ 'style="color:red;;font-weight:bold;;text-align:center"' }elsif( $READING =~ m/humidity/ && $VALUE < 60 ){ 'style="color:blue;;text-align:center"' }else{ 'style="color:black;;text-align:center"' }
}
attr RoomInfo valueSuffix { temperature => " °C", temperature_max_day => " °C", temperature_min_day => " °C", humidity => " %", humidity_max_day => " %", humidity_min_day => " %", absFeuchte => " %", dewpoint => " °C" }

I will leave the first two attempts here, they may provide useful examples for those as confused by readingsGroups as me ;)

Here's yet another version with an added timestamp to keep track of one sensor that wasn't always getting data through to fhem.
All reasonably simple...

define rg_RoomInfo readingsGroup <%status_comfort>,<Temperature>,<|>,<Max temp>,<|>,<Min  temp>,<|>,<rHumidity>,<|>,<Max hum>,<|>,<Min\n  hum>,<|>,<aHumidity>,<|>,<Dewpoint>,<|>,<TimeStamp> \
<hr> \
SWAP.(0A|0B|0C|0D|0E|0F):,temperature,<|>,temperature_max_day,<|>,temperature_min_day,<|>,humidity,<|>,humidity_max_day,<|>,humidity_min_day,<|>,absFeuchte,<|>,dewpoint,<|>,<{ReadingsTimestamp( $DEVICE, "temperature", "")}>
attr rg_RoomInfo mapping %ROOM
attr rg_RoomInfo nameStyle style="color:black;font-weight:bold"
attr rg_RoomInfo valueFormat { temperature => "%.1f", temperature_max_day => "%.1f", temperature_min_day => "%.1f", humidity => "%.1f", humidity_max_day => "%.1f", humidity_min_day => "%.1f", absFeuchte => "%.1f", dewpoint => "%.1f" }
attr rg_RoomInfo valueStyle { if( $READING =~ m/temperature/ && $VALUE > 23 ){ 'style="color:red;font-weight:bold;text-align:center"' }elsif( $READING =~ m/temperature/ && $VALUE > 21 ){ 'style="color:orange;font-weight:bold;text-align:center"' }elsif( $READING =~ m/temperature/ && $VALUE <= 20 ){ 'style="color:blue;text-align:center"' }elsif( $READING =~ m/humidity/ && $VALUE > 65 ){ 'style="color:red;font-weight:bold;text-align:center"' }elsif( $READING =~ m/humidity/ && $VALUE < 60 ){ 'style="color:blue;text-align:center"' }else{ 'style="color:black;text-align:center"' } }
attr rg_RoomInfo valueSuffix { \
if ( $READING =~ /temperature/ || $READING =~ /dewpoint/ ) { return " °C" } \
elsif ( $READING =~ /humidity/ || $READING =~ /absFeuchte/ ) { return " %" } \
}

However the timestamp is displayed in bold text, taken from the nameStyle attribute "everything that is not a reading and has no name (i.e. heading elements) is affected by nameStyle instead of valueStyle"

Removing the bold entry in attribute nameStyle and adding

attr rg_RoomInfo cellStyle { "r:1" => 'style="color:black;font-weight:bold"' }

solves this by setting just ROW1 to bold leaving the rest as normal.

There is another way around this using a !<pseudo variable> - and I'm adding the code here in case I need it again...

define rg_RoomInfo readingsGroup <%status_comfort>,<Temperature>,<|>,<Max temp>,<|>,<Min temp>,<|>,<rHumidity>,<|>,<Max hum>,<|>,<Min hum>,<|>,<aHumidity>,<|>,<Dewpoint>,<|>,<TimeStamp> \
<hr> \
SWAP.(0A|0B|0C|0D|0E|0F):,temperature,<|>,temperature_max_day,<|>,temperature_min_day,<|>,humidity,<|>,humidity_max_day,<|>,humidity_min_day,<|>,absFeuchte,<|>,dewpoint,<|>,!timestamp
attr rg_RoomInfo mapping %ROOM
attr rg_RoomInfo nameStyle style="color:black;font-weight:bold;text-align:center"
attr rg_RoomInfo valueFormat { temperature => "%.1f", temperature_max_day => "%.1f", temperature_min_day => "%.1f", humidity => "%.1f %%", humidity_max_day => "%.1f", humidity_min_day => "%.1f", absFeuchte => "%.1f", dewpoint => "%.1f", 'timestamp' => ReadingsTimestamp( $DEVICE, "temperature", "") }
attr rg_RoomInfo valueStyle { if( $READING =~ m/temperature/ && $VALUE > 23 ){ 'style="color:red;;font-weight:bold;text-align:center"' }elsif( $READING =~ m/temperature/ && $VALUE > 21 ){ 'style="color:orange;font-weight:bold;text-align:center"' }elsif( $READING =~ m/temperature/ && $VALUE <= 20 ){ 'style="color:blue;text-align:center"' }elsif( $READING =~ m/humidity/ && $VALUE > 65 ){ 'style="color:red;;font-weight:bold;text-align:center"' }elsif( $READING =~ m/humidity/ && $VALUE < 60 ){ 'style="color:blue;text-align:center"' }else{ 'style="color:black;text-align:center"' } }
attr rg_RoomInfo valueSuffix { \
if ( $READING =~ /temperature/ || $READING =~ /dewpoint/ ) { return " °C" } \
elsif ( $READING =~ /humidity/ || $READING =~ /absFeuchte/ ) { return " %" } \
}

The timestamp string is now formatted using the valueStyle attribute.


I have tidied up the multiple pairs of semicolons that were here before.

Generally a single semicolon is all that is needed in code as you edit in the fhem web page fields however this will appear (correctly) in your fhem.cfg as a pair of semicolons.



Device Status

It's now October and my heating has come on a few times - but my bedroom radiator is still cold!

Looking more closely the device display is blank, the batteries have died and I have had no warning mail!

I guess that must mean they died rapidly without sending 'low' status. The device Readings still show battery ok too but with a Timestamp back in August.

There is a useful bit of code for 99_myUtils.pm at http://www.fhemwiki.de/wiki/Batterieüberwachung but this only checks the state Timestamp which is up to date for this device.

I have modified it a little so that I can check the Timestamp of any one of the Readings I choose.

#########################################################
#### Monitor Devices
#########################################################
sub check_if_alive($$$)
{
 # Expects:
 # 1. Devicename to be checked
 # 2. Age in hours, after the expiry of which with no new state the device will be considered as dead.
 # Returns:
 # 0 -> Device dead
 # 1 -> Device alive
 # 2 -> Error
 my ( $DeviceType, $reading, $hours_threshold ) = @_;
 my $now = time;

 foreach my $dev ( sort keys %defs )
 {
  if( $defs{$dev}{TYPE} eq "$DeviceType" )
  {
   # Check for TRX_LIGHT && PIR, ignore others
   if( $defs{$dev}{TYPE} eq "TRX_LIGHT" && $defs{$dev}{NAME} !~ /PIR/ )
   {
    next
   }
   else
   {
    Log 5, ( "check_if_alive: check Device: $defs{$dev}{NAME}" );
    my $Timestamp = ReadingsTimestamp( $defs{$dev}{NAME}, $reading, "0" );
    if ( $Timestamp ne "0" )
    {
     my @splitdatetime = split( / /, $Timestamp );
     my @splitdate = split( /-/, $splitdatetime[0] );
     my @splittime = split( /:/, $splitdatetime[1] );
     my $last_state_time = timelocal( $splittime[2], $splittime[1], $splittime[0], $splitdate[2], $splitdate[1]-1, $splitdate[0] );
     my $age_in_hours = ( $now - $last_state_time ) / 3600;

     if ( $age_in_hours > $hours_threshold )
     {
      DebianMail( 'mail@example.com', 'FHEM Device warning', "$defs{$dev}{NAME} $reading dead, last change was $age_in_hours hours ago" );
      Log 1, ( "check_if_alive: $defs{$dev}{NAME} $reading dead, last change was $age_in_hours hours ago" );
     }
    }
    else
    {
     Log 4, ( "check_if_alive: $defs{$dev}{NAME} has no Timestamp" );
    }
   }
  }
 }
}
#########################################################
#### END Monitor Devices
#########################################################

This can be called with something like..

define MAX_check at *04:30:00 { check_if_alive( "MAX", "battery", 12 ) }

and should provide me with another method of warning.
I have added another for my PIRs, it's easy to forget them..

define TRX_check at *04:35:00 { check_if_alive( "TRX_LIGHT", "state", 24 ) }

however this will check all TRX_LIGHT devices, including sockets and the random devices that HomeEasy devices create.

All I want to do is check the status of the PIRs and this is tested for in the not-match statement (!~).



As an aside but related to batteries, I came across http://www.batteryshowdown.com/index.html the other day.

Very interesting, and I'm going to try some of the cheaper brands listed.

Some of the big brands seem to be trying to confuse everyone by bringing out more and more variations on their main line, all with slightly different names :(



77_UWZ.pm Weather Warnings

This section explains how to set up 77_UWZ.pm Weather Warning module to enable UK weather warnings from http://www.severe-weather-centre.co.uk as mentioned here.


First of all we need to find the relevant parameters for our own location.

Using the suggestions in the wiki let's try Stansted Airport again as an example, so this URL

http://alertspro.geoservice.meteogroup.de/weatherpro/SearchFeed.php?search=stansted

produces

<?xml version="1.0" encoding="UTF-8" standalone="no"?><result>
 <cities previousOffset="-1" nextOffset="-1">
  <city country="44" city="1251" id="18144003" country-name="United Kingdom" province-name="England" continent="6" inhabitants="35000" longitude="0.18948" latitude="51.26663" name="Stansted (Sevenoaks)" timezone="Europe/London"/>
  <city country="44" city="1330" id="18144082" country-name="United Kingdom" province-name="England" continent="6" inhabitants="7500" longitude="0.22461" latitude="51.88257" name="Stansted" timezone="Europe/London"/>
 </cities>
 <pois>
  <poi country="44" id="1668780" country-name="United Kingdom" continent="6" longitude="-0.916667" latitude="50.883333" name="Stansted Park" timezone="Europe/London"/>
  <poi country="44" id="16866800" country-name="United Kingdom" continent="6" longitude="-0.2333" latitude="51.8833" name="Stansted" timezone="Europe/London"/>
 </pois>
</result>

It's not immediately obvious which one is correct however the second is close enough. (If you see a completely blank page check the page source, your browser may simply be hiding the results!)

Using the latitude and longitude from this result we now use this URL

http://feed.alertspro.meteogroup.com/AlertsPro/AlertsProPollService.php?method=lookupCoord&lat=51.88257&lon=0.22461

which produces

[{"AREA_TYPE":"UWZ","AREA_ID":"UWZUK01731","CENTER_ID":"2"}]

and we have an ID to use in the fhem definitions for the details and maps

define Unwetterzentrale UWZ UK 01731 3600
attr Unwetterzentrale URL http://feed.alertspro.meteogroup.com/AlertsPro/AlertsProPollService.php?method=getWarning&language=en&areaID=UWZUK01731
attr Unwetterzentrale download 1
attr Unwetterzentrale maps eastofengland unitedkingdom

define UnwetterMapEastUK weblink htmlCode {UWZAsHtmlKarteLand("Unwetterzentrale","eastofengland")}

define UnwetterMapUK weblink htmlCode {UWZAsHtmlKarteLand("Unwetterzentrale","unitedkingdom")}

Available UK maps are:

  • unitedkingdom
  • eastofengland
  • eastmidlands
  • london
  • northeastengland
  • northernireland
  • northwestengland
  • scotland
  • southeastengland
  • southwestengland
  • wales
  • westmidlands
  • yorkshireandthehumber
  • isobaren3

isobaren3 is a map of isobars across Europe with English language legend.


I have set up a mail alert for weather warnings by adding the following code to 99_myUtils.pm

#####################################################################
####    Weather Warning Mail
#####################################################################
sub uwzWarnMail($)
{
 my ($name) = @_;
 
 my ( $wType, $wText, $wStart, $wEnd );
 my $msg = "";
 
 my $wNb = ReadingsVal( $name, "WarnCount", 0 ) - 1;
 
 for my $i ( 0 .. $wNb )
 {
  $wType = ReadingsVal( $name, "Warn_${i}_Type_Str", 0 );
  $wText = ReadingsVal( $name, "Warn_${i}_ShortText", 0 ) . '\n\n' . ReadingsVal( $name, "Warn_${i}_LongText", 0 );

  $wStart = localtime( ReadingsVal( $name, "Warn_${i}_Start", 0 ) );
  $wEnd = localtime( ReadingsVal( $name, "Warn_${i}_End", 0 ) );

  $wType = "Storm" if ( $wType eq "Sturm" );
  $wType = "Snow" if ( $wType eq "Schnee" );
  $wType = "Rain" if ( $wType eq "Regen" );
  $wType = "Thunderstorms" if ( $wType eq "Gewitter" );
  $wType = "Freezing rain" if ( $wType eq "Glatteisregen" );
  
  $msg .= $wType . '\n\n' . $wText . '\n\nWarning start time: ' . $wStart . '\nWarning end time:  ' . $wEnd;
  $msg .= '\n===============================\n\n';
 }
 DebianMail( 'your-mail-address@example.com', 'FHEM Weather Warning', $msg );
}
#####################################################################
####    END Weather Warning Mail
#####################################################################

Note that the German text $wType warnings eg. Gewitter is replaced with English Thunderstorms.
I have added a few other warnings that require similar translations but I won't know if they are correct until I receive the relevant warning.

and this is the notify definition

define notifyUnwetterwarning notify Unwetterzentrale:WarnCount:.[1-9] {\
 uwzWarnMail( $NAME )\
}





Just a short note that I have tidied these pages a lot so hope there are not too many errors - or bits missing!



Hint: If you copy and paste bits of the code from this page into the fhem web page fields you may end up with instances of two pairs of semicolons in fhem.cfg!




Last updated 27-07-2016



All trademarks referred to in this web site are the properties of their respective owners. Trademarked names appear throughout the content of this site. Trademarks, and their respective owners, are not systematically listed or marked in the text, but nevertheless all trademarks are acknowledged. Any trademarks or names being used are for editorial purposes only, and to the benefit of the trademark owner, with no intention of infringing upon that trademark.

© 2009-2016 fruit
Page design by fruit.

Valid HTML 4.0! Document made with Nvu
Home My work My computers Links page Genealogy pages Bits & pieces

This page
Contents