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,<br>
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
###
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
#########################################################
#########################################################
#########################################################
# Please do not use this code, it got very broken when I attempted to improve it
# and before I stopped updating this site. (my ISP disabled use of rsync)
# Sorry to anyone caught out by it
#########################################################
#########################################################
#########################################################
# 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
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"
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 temp>,temperature_max_day \
WeatherStation:<%temp_temperature_min>,<Min temp>,temperature_min_day \
WeatherStation:<%weather_humidity>,<Humidity>,humidity \
WeatherStation:<%temperature_humidity>,<Dewpoint>,dewpoint \
WeatherStation:<%temp_windchill>,<Windchill>,windChill \
WeatherStation:<%weather_wind_speed>,<Wind 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 days>,state \
RainThisYear:<%weather_rain_gauge>,<This 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
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
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.
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 :(
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!
|