element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      • Japan
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Vietnam
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Pi IoT
  • Challenges & Projects
  • Design Challenges
  • Pi IoT
  • More
  • Cancel
Pi IoT
Blog PiIoT - DomPi 09: Presence Emulator
  • Blog
  • Forum
  • Documents
  • Polls
  • Files
  • Events
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: mg.sergio
  • Date Created: 4 Aug 2016 9:23 PM Date Created
  • Views 516 views
  • Likes 1 like
  • Comments 0 comments
  • piiot
  • piiot challenge
Related
Recommended

PiIoT - DomPi 09: Presence Emulator

mg.sergio
mg.sergio
4 Aug 2016

These last two weeks have been quite hectic in terms of progressing with DomPi. My first intention was to develop most of the features and functionalities in C++ modules which would connect among them and with the openHAB via Mosquitto messages. When I was half way through it, I have gladly discovered that most of the C coding could be performed directly with the openHAB via the rules and actions that this platform supports.

 

What were the implications? A complete redesign of the DomPi development in this area... and investing long hours and days on understanding how to get code done in openHAB. However, my expectation is that the final result will be much more robust and scalable than the previous C++ modules-approach. Time will tell!!

 

This post covers three key components:

  • the C++ module that connects the external RF24 messaging with the internal mosquitto channels,
  • the initial openHAB sitemaps, items and rules
  • the Presence Emulator feature to simulate that I am at home instead of enjoying my vacations

 

Previous Posts

PiIoT - DomPi: ApplicationPiIoT - DomPi: IntroPiIoT - DomPi 02: Project Dashboard and first steps in the Living room
PiIoT - DomPi 03: Living room, light control via TV remotePiIoT - DomPi 04: Movement detection and RF2.4Ghz commsPiIoT - DomPi 05: Ready for use Living Room and parents and kids´ bedrooms
PiIoT - DomPi 06: Setting up the Command Center - RPI3PiIoT - DomPi 07: Setting up the Command Center (2)PiIoT - DomPi 08: Setting up the Command Center (3) openHAB, mosquitto, RF24

 

Project Status

image

 

RF24 C++ Gateway

This module governs the RF board, enabling the communication with the remote nodes. It will receive and send any data and commands at DomPi. Internally, it interfaces with the Mosquitto hub in the Command Center (RPI3), subscribing and publishing to the relevant mqtt channels. Via these channels it shares the information received from the nodes with the openHAB and gets commands from it - like turn on/off the lights, provide updated temperature, humidity or movement detection.

 

As a quick summary, this is the architecture in a nutshell - further details at PiIoT - DomPi 08: Setting up the Command Center (3) openHAB, mosquitto, RF24:

Here is the code:image

#include <RF24/RF24.h>
#include <RF24Network/RF24Network.h>
#include </usr/include/mosquittopp.h>
#include </usr/include/mosquitto.h>
#include <iostream>
#include <ctime>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <list>
#include "Defs_y_Clases.h"

/**
 * g++ -L/usr/lib main.cc -I/usr/include -o main -lrrd
 **/

// Constants that identify nodes
const uint16_t pi_node = 0;
const uint16_t sensor_node1 = 1; //Arduino at Kids bedroom: temp, hum, luminosity
const uint16_t action_node1 = 1; //Arduino at Kids bedroom: temp, hum, luminosity
const uint16_t sensor_node2 = 2;
const uint16_t action_node2 = 4;
const uint16_t sensor_node3 = 3;  //Salon IR
const uint16_t action_node3 = 3;  //Salon IR

const char* action_channel1_lamp  = "casa/habebes/luz";      //kids bedroom, channel for the light
const char* action_channel2_lamp  = "casa/dormitorio/luz";    //parents bedroom
const char* action_channel3_lamp  = "casa/salon/luz";        //Living room
const char* action_channel4_lamp  = "casa/pasillo/luz";        //Corridor 
const char* action_channel3_force = "casa/salon/forzar";    //Living room - force message
const char* action_channel_casa   = "casa/#";

int num_RF_node1 = 0;    //Number of RF messages received from node 1. For testing purposes

#define TODAS_LUCES_TEMP     88
// CE Pin, CSN Pin, SPI Speed
// Old configRF24 radio(RPI_BPLUS_GPIO_J8_32,RPI_BPLUS_GPIO_J8_26, BCM2835_SPI_SPEED_8MHZ);
RF24 radio(RPI_BPLUS_GPIO_J8_15,RPI_BPLUS_GPIO_J8_24, BCM2835_SPI_SPEED_8MHZ);
RF24Network network(radio);

// Time between checking for packets (in ms)
const unsigned long interval = 1000;

// Structure of our messages
//Message to be sent to the remote nodes
struct message_action {
    unsigned char cmd;
    unsigned char info;
};

//Message to be received from the remote nodes
struct message_sensor {
    int16_t temperature;
    unsigned char humidity;
    unsigned char light;
    unsigned char motion;
    unsigned char dooropen;        //Not used, future purposes
};


//Main loop takes actions but do not execute right away
//actions to be sent are stored in a list and processed afterwards
struct elem_lista {
    unsigned char cmd;
    unsigned char info;
    uint16_t target_node;
    char* topic;
};

// List of message_action to process
std::list<elem_lista> lista_acciones;

//Auxiliary functions
uint16_t seleccionar_nodo(char* topic) {
  //Identifies to wich node to send the message
  uint16_t target_node=100;

  if (strcmp(topic, action_channel4_lamp) == 0) {
    //si es la lampara del pasillo, envio el mensaje al nodo 3, salon, para que actue
    target_node = action_node3;
  } else if (strcmp(topic, action_channel3_lamp) == 0) {
    //si es la lampara del salon, envio el mensaje al nodo 3, salon, para que actue
    target_node = action_node3;
  } else if (strcmp(topic, action_channel3_force) == 0) {
    //si es la lampara del salon, envio el mensaje al nodo 3, salon, para que actue
    target_node = action_node3;
  } else if (strcmp(topic, action_channel2_lamp) == 0) {
    //si es la lampara del dormitorio, envio el mensaje al nodo 3, salon, para que actue
    target_node = action_node3;
  } else if (strcmp(topic, action_channel1_lamp) == 0) {
    //si es la lampara de las ninnas, envio el mensaje al nodo 3, salon, para que actue
    target_node = action_node3;
  }
  return target_node;
}

unsigned char determinar_cmd(char* payload) {
  //Determines the command that has been received via RF24
  int load = atoi(payload);
  unsigned char val=100;

  switch (load) {
    case 0:  //Ojo, es lo que envia Openhab a traves de mosquitto. 
         //Suele ser 0 o 1 pero puedo modificarlo en default items
      val = LUZ_OFF;
      break;
    case 1:
      val = LUZ_ON;
      break;
    case FORCE_SEND_MSG:
      val = FORCE_SEND_MSG;
      break;
    default:
      printf("Unknown message: %s\n", payload);
      val = 100;
      break;
  }
  return val;
}

unsigned char determinar_info(char* topic) {
  //Determine info to transmit
  unsigned char info = NO_ACTION;

  if (strcmp(topic, action_channel1_lamp) == 0) {
    //si es la lampara de las ninnas, LUZ_1
    info = LUZ_1;
  } else if (strcmp(topic, action_channel2_lamp) == 0) {
    //si es la lampara del dormitorio, LUZ_2
    info = LUZ_2;
  } else if (strcmp(topic, action_channel3_lamp) == 0) {
    //si es la lampara del salon, LUZ_3
    info = LUZ_3;
  } else if (strcmp(topic, action_channel3_force) == 0) {
    //si solicito forzar el envio de datos de sensores
    //la info en si es irrelevante
    info = FORCE_SEND_MSG;
  } else if (strcmp(topic, action_channel4_lamp) == 0) {
    //si solicito lamp en canal 4/pasillo quiere decir que es TODAS LUCES
    //Uso valor temporal, pq en fiuncion del cmd que ya haya determinado, 
    //tendre que darle On/OFF
    info = TODAS_LUCES_TEMP;
  }
  return info;
}

void enviar_msg_RF24_aux(uint16_t target_node, unsigned char cmd, unsigned char info) {
  // Create message to send via RF24
  message_action actionmessage;
  actionmessage = (message_action){ cmd, info };

  // Send message on RF24 network
  RF24NetworkHeader header(target_node);
  header.type = '2';
  printf("Sending instructions to node %i. Header.type: %c. Cmd: %i, Info: %i\n", target_node, header.type, cmd, info);
  if (network.write(header, &actionmessage, sizeof(actionmessage))) {
    printf("Message sent\n"); 
  } else {
    printf("Could not send message\n"); 
  }
}

void enviar_msg_RF24_todasluces(uint16_t target_node, unsigned char cmd) {
  enviar_msg_RF24_aux(target_node, cmd, LUZ_3);
  delay(300);
  enviar_msg_RF24_aux(target_node, cmd, LUZ_2);
  delay(300);
  enviar_msg_RF24_aux(target_node, cmd, LUZ_1);
}

void enviar_msg_RF24(char* topic, uint16_t target_node, unsigned char cmd, unsigned char info) {
  //Compruebo si es necesario accion especial, como con luces pasillo que apaga todas
  if (strcmp(topic, action_channel4_lamp) == 0) {   
    enviar_msg_RF24_todasluces(target_node, cmd);
  } else {
    enviar_msg_RF24_aux(target_node, cmd, info);
  }
}

void revisar_cola() {
  elem_lista e;

  while (lista_acciones.size()>0) {
    e = lista_acciones.front();
    lista_acciones.pop_front();
    enviar_msg_RF24(e.topic, e.target_node, e.cmd, e.info);
  }
}

// Mosquitto class
 class MyMosquitto : public mosquittopp::mosquittopp {
    public:
        MyMosquitto() : mosquittopp::mosquittopp ("PiBrain") { MyMosquitto::lib_init(); }

        virtual void on_connect (int rc) { printf("Connected to Mosquitto\n"); }

        virtual void on_disconnect () { printf("Disconnected\n"); }

        virtual void on_message(const struct mosquitto_message* mosqmessage) {
            unsigned char cmd = 0; //NO_ACTION;
            unsigned char info = 0; //NO_ACTION;

            // Message received on a channel we subscribe to
            //printf("Message found on channel %s: %s\n", mosqmessage->topic, mosqmessage->payload);
            printf("Message found on channel %s", mosqmessage->topic);
            //Extraer datos del mensaje Mosquitto recibido
            //Lo primero es ignorar los mensajes si son "retained": vamos,
            //los ignoro si son mensajes almacenados y no nuevos mensajes
            if (mosqmessage->retain) {
                //If a mosquitto message is marked as retain, it is ignored
                printf("Mensaje ignorado - marcado como retained.\n");
                return;
            }

            //Determinar cmd recibido
            cmd = determinar_cmd((char*)mosqmessage->payload);
            if (cmd==100) return;  //en el script original, si era cmd desconocido hace return

            //Determinar la info a transmitir
            info = determinar_info(mosqmessage->topic);
            if (info==FORCE_SEND_MSG) cmd = FORCE_SEND_MSG; //Arreglo el problema de que el FORCE_SEND_MSG no lo envia el openhab
            if (info==TODAS_LUCES_TEMP) {
              //Si es todas luces, ahora tengo que ver si el cmd era ON u OFF
              //y poner TODAS_LUCES_ON / _OFF
              if (cmd == LUZ_ON) info = TODAS_LUCES_ON;
              else if (cmd == LUZ_OFF) info = TODAS_LUCES_OFF;
            }
            // Determine target node based on channel
            uint16_t target_node;
            target_node = seleccionar_nodo(mosqmessage->topic);

            elem_lista e;
            e.cmd =  cmd;
            e.info =  info;
            e.target_node = target_node;

            //strncpy(e.topic, mosqmessage->topic, sizeof(mosqmessage->topic));
            e.topic = mosqmessage->topic;

            lista_acciones.push_back(e);

        }
 };

MyMosquitto mosq;

void suscribirse_canales() {
  int val;
  val = mosq.subscribe(0, action_channel1_lamp);
  if (val!=MOSQ_ERR_SUCCESS) printf("Hubo error al subscribirse a canal mosquitto 1. ");
  val = mosq.subscribe(0, action_channel2_lamp);
  if (val!=MOSQ_ERR_SUCCESS) printf("Hubo error al subscribirse a canal mosquitto 2. ");
  val = mosq.subscribe(0, action_channel3_lamp);
  if (val!=MOSQ_ERR_SUCCESS) printf("Hubo error al subscribirse a canal mosquitto 3. ");
  val = mosq.subscribe(0, action_channel4_lamp);
  if (val!=MOSQ_ERR_SUCCESS) printf("Hubo error al subscribirse a canal mosquitto 4. ");
  val = mosq.subscribe(0, action_channel3_force);
  if (val!=MOSQ_ERR_SUCCESS) printf("Hubo error al subscribirse a canal mosquitto 3 Forzar. ");
}

void borrarse_canales() {

  mosq.unsubscribe(0, action_channel1_lamp);
  mosq.unsubscribe(0, action_channel2_lamp);
  mosq.unsubscribe(0, action_channel3_lamp);
  mosq.unsubscribe(0, action_channel4_lamp);
  mosq.unsubscribe(0, action_channel3_force);
  printf("Borrado de los canales\n");
}

int main(int argc, char** argv)
{
    // Initialize all radio related modules
    radio.begin();
    delay(50);
    radio.setPALevel(RF24_PA_MAX);
    delay(50);
    radio.setDataRate(RF24_250KBPS);
    delay(50);
    radio.setChannel(108);
    delay(50);
    //radio.enableDynamicPayloads();
    //radio.setPayloadSize(6);
    delay(5);
    network.begin(90, pi_node);

    // Print some radio details (for debug purposes)
    radio.printDetails();

    network.update();

    mosq.connect("127.0.0.1");
    suscribirse_canales(); //mosq.subscribe(0, action_channel_casa);

    while (true) {
        // Get the latest network info
        network.update();
        //printf(".");
        // Enter this loop if there is data available to be read,
        // and continue it as long as there is more data to read
        while ( network.available() ) {
            RF24NetworkHeader header;
            message_sensor sensormessage;
            // Have a peek at the data to see the header type
            network.peek(header);
            // We can only handle type 1 sensor nodes for now
            if (header.type == '1') {
                // Read the message
                network.read(header, &sensormessage, sizeof(sensormessage));
                // Print it out in case someone's watching
                printf("Data received from node %i\n", header.from_node);
                if (header.from_node==1) {
                    num_RF_node1++;
                    printf(" .. Message to the previous node: %i\n", num_RF_node1);
                }
                char buffer [50];
                float temperature_rx = sensormessage.temperature/100.0;
                //printf("---Test Temperatura_rf: %i \n", sensormessage.temperature);
                //printf("---Test Temperatura_rx: %f \n", temperature_rx);
                switch (header.from_node) {
                    case sensor_node1:
                        sprintf (buffer, "mosquitto_pub -t casa/habebes/temperatura -m \"%f\"", temperature_rx);
                        system(buffer);
                        sprintf (buffer, "mosquitto_pub -t casa/habebes/humedad -m \"%i\"", sensormessage.humidity);
                        system(buffer);
                        sprintf (buffer, "mosquitto_pub -t casa/habebes/luminosidad -m \"%i\"", sensormessage.light);
                        system(buffer);
                        break;
                    case sensor_node2:
                        sprintf (buffer, "mosquitto_pub -t casa/dormitorio/temperatura -m \"%f\"", temperature_rx);
                        system(buffer);
                        sprintf (buffer, "mosquitto_pub -t casa/dormitorio/humedad -m \"%i\"", sensormessage.humidity);
                        system(buffer);
                        sprintf (buffer, "mosquitto_pub -t casa/dormitorio/luminosidad -m \"%i\"", sensormessage.light);
                        system(buffer);
                        break;
                    case sensor_node3:
                        //printf("Nodo Salon. Temperatura: %f Humedad %i Luminosidad %i \n", temperature_rx, sensormessage.humidity, sensormessage.light);
                        //sprintf (buffer, "mosquitto_pub -t casa/salon/temperatura -m \"%f\"", temperature_rx);
                        printf("Nodo Salon. Temperatura: %f Humedad %i Luminosidad %i Movimiento %i\n", temperature_rx, sensormessage.humidity, sensormessage.light, sensormessage.motion);
                        sprintf (buffer, "mosquitto_pub -t casa/salon/temperatura -m \"%f\"", temperature_rx);
                        system(buffer);
                        sprintf (buffer, "mosquitto_pub -t casa/salon/humedad -m \"%i\"", sensormessage.humidity);
                        system(buffer);
                        sprintf (buffer, "mosquitto_pub -t casa/salon/luminosidad -m \"%i\"", sensormessage.light);
                        system(buffer);
                        sprintf (buffer, "mosquitto_pub -t casa/salon/movimiento -m \"%i\"", sensormessage.motion);
                        system(buffer);
                        break;
                    default:
                        printf("Unknown node %i\n", header.from_node);
                        break;
                }
            } else if (header.type == '2') {
                message_action actionmessage;
                network.read(header, &actionmessage, sizeof(actionmessage));
                unsigned char cmd_rx = actionmessage.cmd;
                unsigned char info_rx = actionmessage.info;
                printf("Message received from node %i header tipo 2. Cmd: %i Info:%i\n", header.from_node, cmd_rx, info_rx);
                
            } else {
                // This is not a type we recognize
                network.read(header, &sensormessage, sizeof(sensormessage));
                float temperature_rx = sensormessage.temperature/100;
                printf("Unknown message received from node %i Header type: %i\n", header.from_node, header.type);
                printf("Nodo Salon. Temperatura: %f Humedad %i Luminosidad %i \n", temperature_rx, sensormessage.humidity, sensormessage.light);
            }
        }
        
        // Check for messages on our subscribed channels
        mosq.loop();
        revisar_cola();
        
        delay(interval);
    }
    
    borrarse_canales(); //mosq.unsubscribe(0, action_channel_casa);

    // last thing we do before we end things
    return 0;

}

 

In order to compile it, I´m using this Makefile and build it via the "make" command in the RPI:

#############################################################################
#
# Makefile for librf24 examples on Raspberry Pi
#
# License: GPL (General Public License)
# Author:  gnulnulf <arco@appeltaart.mine.nu>
# Date:    2013/02/07 (version 1.0)
#
# Description:
# ------------
# use make all and make install to install the examples
# You can change the install directory by editing the prefix line
#
prefix := /usr/local

# The recommended compiler flags for the Raspberry Pi
CCFLAGS=-Ofast -mfpu=vfp -mfloat-abi=hard -march=armv7-a -mtune=arm1176jzf-s
#CCFLAGS=

# define all programs
PROGRAMS = cc_RF2mqtt
SOURCES = ${PROGRAMS:=.cpp}

all: ${PROGRAMS}

${PROGRAMS}: ${SOURCES}
    g++ ${CCFLAGS} -Wall -I../ -lrf24-bcm -lrf24network -lmosquittopp $@.cpp -o $@

clean:
    rm -rf $(PROGRAMS)

install: all
    test -d $(prefix) || mkdir $(prefix)
    test -d $(prefix)/bin || mkdir $(prefix)/bin
    for prog in $(PROGRAMS); do \
      install -m 0755 $$prog $(prefix)/bin; \
    done

.PHONY: install

 

Some explanation of the code above

My first call out is that the code is still too much in the development phase, meaning for example, it requires lots of tweaks to enable the communication with all of the remote nodes - it is in testing mode and works perfectly with a couple of them, it should be just a matter of time. All in all, there will be several improvements made in the remaining weeks and posts of the challenge.

 

High level, the module listens to any new messages coming via

  • Mosquitto - see the on_message function from the Mosquitto class. When a mqtt message is received, its action is stored in a list of actions to be processed later on in the main loop. Perhaps this could be "not a must" as potentially you could execute the action right away. However, I want to break this automation and put additional controls in the main loop.
  • RF24 - the code processes the RF message within the while loop (network.available()). As soon as the RF24 message is received, it is processed by checking which node sent it (living room, bedrooms, etc), what type of information it is carrying (header.type) and if it is recognized then a mqtt message is sent to the appropriate channel to share the information with openHAB.

The rest of the code should be self-explanatory.

 

Command Center - Control with openHAB

Rather than having tens of C-modules for developing the DomPi features, like the Presence Emulator or the Car Presence, I will be leveraging the rule engine. This has slowed down the DomPi progress as... I am discovering this as we go! But the final solution looks promising.

 

During this week, I have added more items to the design in the Post 8, as well as redesigned the sitemap. Now it just looks better, hope you agree with me! image Some snapshots here:

imageimage

 

 

Items file:

Group All
Group gPiso        (All)
Group gLuces    (All)
Group gTempers    (All)
Group gLuminos    (All)
Group gHumidit    (All)
Group gMotions    (All)
Group gStatus        "Estado General"    (All)
Group gControls        "Controls"            (All)
Group gPE        "Presence Emulator Schedule"        (All)
Group gPE1        "Kids Room"                (gPE)
Group gPE2        "Parents Room"            (gPE)
Group gPE3        "Living Room"            (gPE)

Group P_Salon         "Salon"             <video>     (gPiso)
Group Dormitorio     "Dormitorio"         <video>     (gPiso)
Group Pasillo         "Pasillo"             <video>     (gPiso)
Group Habebes         "Habitacion Bebes"     <video>     (gPiso)
Group gGarage         "Garage"             <video>     (gPiso)
Group gGarden         "Terraza"             <video>     (gPiso)


/* active groups */
Group:Switch:OR(ON, OFF)         Lamparas        "All Lights [(%d)]"                                        (All)
Group:Number:AVG                 Temperaturas    "Temperatura Media Interior[%.1f °C]"     <temperature>    (gStatus)
Group:Number:AVG                 Humedades        "Humedad Media Interior[%.1f °C]"         <bath>            (gStatus)
Group:Number:AVG                 Luminosidades    "Luminosidad Media Interior[%.1f °C]"     <sun>            (gStatus)
Group:Switch:OR(ON, OFF)         Movimientos     "Movimientos actuales [(%d)]"             <shield>         (All)


Group:Switch:OR(ON, OFF)         Lights         "All Lights [(%d)]"                                 (All)
Group:Switch:OR(ON, OFF)         Heating     "No. of Active Heatings [(%d)]"     <heating>         (All)
Group:Number:AVG                 Temperature    "Avg. Room Temperature [%.1f °C]"     <temperature>    (Status)
Group:Contact:OR(OPEN, CLOSED)     Windows     "Open windows [(%d)]"                <contact>        (All)

/* Parents Bedroom */
Number Nodo02Temperatura    "Temperatura [%.1f C]"     <temperature>     (Dormitorio, gTempers)     {mqtt="<[dompimqtt:casa/dormitorio/temperatura:state:default]"}
Number Nodo02Humedad         "Humedad [%d %%]"         <bath>             (Dormitorio, gHumidit)     {mqtt="<[dompimqtt:casa/dormitorio/humedad:state:default]" }
Number Nodo02Luminosidad     "Luminosidad [%d %%]"     <sun>             (Dormitorio, gLuminos)     {mqtt="<[dompimqtt:casa/dormitorio/luminosidad:state:default]" }
Switch Lampara_2            "Luz"                                    (Dormitorio, gLuces)    { mqtt=">[dompimqtt:casa/dormitorio/luz:command:ON:1],>[dompimqtt:casa/dormitorio/luz:command:OFF:0]"}

/* Kids Bedroom */
Number Nodo01Temperatura    "Temperatura [%.1f C]"  <temperature>    (Habebes, gTempers)     {mqtt="<[dompimqtt:casa/habebes/temperatura:state:default]"}
Number Nodo01Humedad        "Humedad [%d %%]"       <bath>           (Habebes, gHumidit)     {mqtt="<[dompimqtt:casa/habebes/humedad:state:default]" }
Number Nodo01Luminosidad    "Luminosidad [%d %%]"   <sun>           (Habebes, gLuminos)        {mqtt="<[casapi:casa/habebes/luminosidad:state:default]" }
Switch Lampara_1            "Luz"                                    (Habebes, gLuces)        { mqtt=">[dompimqtt:casa/habebes/luz:command:ON:1],>[dompimqtt:casa/habebes/luz:command:OFF:0]"}

/* Living Room */
Number Nodo03Temperatura    "Temperatura [%.1f C]"     <temperature>    (P_Salon, gTempers)        { mqtt="<[dompimqtt:casa/salon/temperatura:state:default] "}
Number Nodo03Humedad        "Humedad [%d %%]"       <bath>           (P_Salon, gHumidit)     { mqtt="<[dompimqtt:casa/salon/humedad:state:default] "}
Number Nodo03Luminosidad    "Luminosidad [%d %%]"   <sun>           (P_Salon, gLuminos)     { mqtt="<[dompimqtt:casa/salon/luminosidad:state:default] "}
Number Nodo03Movimiento        "Movimiento [%d]"        <shield>        (P_Salon, gMotions)        { mqtt="<[dompimqtt:casa/salon/movimiento:state:default] "}
Switch Lampara_3            "Luz"                                    (P_Salon, gLuces)         { mqtt=">[dompimqtt:casa/salon/luz:command:ON:1],>[dompimqtt:casa/salon/luz:command:OFF:0]"}

/* Corridor */
Number Nodo04Temperatura    "Temperatura [%.1f C]"    <temperature>    (Pasillo, gTempers)        {mqtt="<[dompimqtt:casa/pasillo/temperatura:state:default] "}
Number Nodo04Humedad        "Humedad [%d %%]"         <bath>            (Pasillo, gHumidit)        {mqtt="<[dompimqtt:casa/pasillo/humedad:state:default] "}
Number Nodo04Luminosidad    "Luminosidad [%d %%]"    <sun>            (Pasillo, gLuminos)        {mqtt="<[dompimqtt:casa/pasillo/luminosidad:state:default] "}
Switch Luces_Casa            "Todas las Luces"                        (Pasillo, gLuces)        { mqtt=">[dompimqtt:casa/pasillo/luz:command:ON:1],>[dompimqtt:casa/pasillo/luz:command:OFF:0]"}

/* Garage */
Number Nodo05Temperatura    "Temperatura [%.1f C]"    <temperature>    (gGarage, gTempers)        {mqtt="<[dompimqtt:casa/garage/temperatura:state:default] "}
Number Nodo05Humedad        "Humedad [%d %%]"         <bath>            (gGarage, gHumidit)        {mqtt="<[dompimqtt:casa/garage/humedad:state:default] "}
Number Nodo05Luminosidad    "Luminosidad [%d %%]"    <sun>            (gGarage, gLuminos)        {mqtt="<[dompimqtt:casa/garage/luminosidad:state:default] "}

/* Garden */
Number Nodo06Temperatura    "Temperatura [%.1f C]"    <temperature>    (gGarden, gTempers)        {mqtt="<[dompimqtt:casa/terraza/temperatura:state:default] "}
Number Nodo06Humedad        "Humedad [%d %%]"         <bath>            (gGarden, gHumidit)        {mqtt="<[dompimqtt:casa/terraza/humedad:state:default] "}
Number Nodo06Luminosidad    "Luminosidad [%d %%]"    <sun>            (gGarden, gLuminos)        {mqtt="<[dompimqtt:casa/terraza/luminosidad:state:default] "}

/* Status */ 
Switch Forzar_tx            "Actualizar"                            (gStatus, gControls)    { mqtt=">[dompimqtt:casa/salon/forzar:command:ON:7],>[dompimqtt:casa/salon/forzar:command:OFF:7]"}

/* Controls */
Switch Presence_Emulator    "Presence Emulator"                        (gControls)

/* Presence Emulator Items */
DateTime        dtLampara_1_on1        "L1 On  [%1$tH:%1$tM]"            (gPE1)
DateTime        dtLampara_1_off1    "L1 Off [%1$tH:%1$tM]"            (gPE1)
DateTime        dtLampara_1_on2        "L1 On  [%1$tH:%1$tM]"            (gPE1)
DateTime        dtLampara_1_off2    "L1 Off [%1$tH:%1$tM]"            (gPE1)
DateTime        dtLampara_2_on1        "L2 On  [%1$tH:%1$tM]"            (gPE2)
DateTime        dtLampara_2_off1    "L2 Off [%1$tH:%1$tM]"            (gPE2)
DateTime        dtLampara_2_on2        "L2 On  [%1$tH:%1$tM]"            (gPE2)
DateTime        dtLampara_2_off2    "L2 Off [%1$tH:%1$tM]"            (gPE2)
DateTime        dtLampara_3_on1        "L3 On  [%1$tH:%1$tM]"            (gPE3)
DateTime        dtLampara_3_off1    "L3 Off [%1$tH:%1$tM]"            (gPE3)
DateTime        dtLampara_3_on2        "L3 On  [%1$tH:%1$tM]"            (gPE3)
DateTime        dtLampara_3_off2    "L3 Off [%1$tH:%1$tM]"            (gPE3)

/* NTP binding demo item */
DateTime        Date            "Date [%1$tH:%1$tM %1$tA, %1$td.%1$tm.%1$tY]"    <calendar>    { ntp="Europe/Madrid" }

/* Internet Weather */
Group Weather_Chart                                                    (Weather)
Number Weather_Temperature         "Temperatura en Krakow [%.1f °C]"    <temperature>     (Weather_Chart) { http="<[http://weather.yahooapis.com/forecastrss?w=638242&u=c:60000:XSLT(yahoo_weather_temperature.xsl)]" }
Number Weather_Humidity         "Outside Humidity [%.1f %%]"        <temperature>     (Weather) { http="<[http://weather.yahooapis.com/forecastrss?w=638242&u=c:60000:XSLT(yahoo_weather_humidity.xsl)]" }
Number Weather_Humidex            "Humidex [SCALE(humidex.scale):%s]"                 (Weather)
Number Weather_Temp_Max         "Todays Maximum [%.1f °C]"            <temperature>     (Weather_Chart)
Number Weather_Temp_Min         "Todays Minimum [%.1f °C]"            <temperature>     (Weather_Chart)
Number Weather_Chart_Period        "Chart Period"
DateTime Weather_LastUpdate        "Last Update [%1$ta %1$tR]"    <clock>

/* Locations */
Location DemoLocation            "Krakow"    

Sitemap file:

 

Sitemap file:

sitemap default label="DomPi - My Domek"
{
    Frame label="Date" {
        Text item=Date
    }
    Frame {
        Switch item=Presence_Emulator mappings=[ON="On", OFF="Off"]
        Group item=gPE visibility=[Presence_Emulator==ON]
    }
    Frame {
        Group item=P_Salon         label="Salon"                 //icon="firstfloor"
        Group item=Dormitorio     label="Dormitorio"             //icon="firstfloor"
        Group item=Habebes         label="Habitacion Bebes"     //icon="firstfloor"
        Group item=Pasillo         label="Pasillo"             //icon="firstfloor"
        Group item=gGarage         label="Garage"                 //icon="firstfloor"
        Group item=gGarden         label="Terraza"             //icon="firstfloor"
    }
    Frame label="Estado" {
        Group item=gStatus
        
    }
    Frame label="El Tiempo" {
        Text item=Weather_Temperature valuecolor=[Weather_LastUpdate=="Uninitialized"="lightgray",Weather_LastUpdate>90="lightgray",>25="orange",>15="green",>5="orange",<=5="blue"] {
            Frame {
                Text item=Weather_Temp_Max valuecolor=[>25="orange",>15="green",>5="orange",<=5="blue"]
                Text item=Weather_Temp_Min valuecolor=[>25="orange",>15="green",>5="orange",<=5="blue"]
                Text item=Weather_Humidity
                Text item=Weather_Humidex
                Text item=Weather_LastUpdate visibility=[Weather_LastUpdate>30] valuecolor=[Weather_LastUpdate>120="orange", Weather_LastUpdate>300="red"]
            }
            Frame {
                Switch item=Weather_Chart_Period label="Chart Period" mappings=[0="Hour", 1="Day", 2="Week"]
                Chart item=Weather_Chart period=h refresh=6000 visibility=[Weather_Chart_Period==0, Weather_Chart_Period=="Uninitialized"]
                Chart item=Weather_Chart period=D refresh=30000 visibility=[Weather_Chart_Period==1]
                Chart item=Weather_Chart period=W refresh=30000 visibility=[Weather_Chart_Period==2]
            }
        }
    }
/*
    Frame label="Conjuntos" {
        Text label="Agrupacion" icon="firstfloor" {
            Switch item=Lamparas mappings=[OFF="All Off"]
            Group item=Movimientos
            Text item=Temperaturas
            Text item=Humedades
            Text item=Luminosidades
        }
        Text label="Widget Overview" icon="chart" {
            Frame label="Binary Widgets" {
                Switch item=DemoSwitch label="Toggle Switch"
                Switch item=DemoSwitch label="Button Switch" mappings=[ON="On", OFF="Off"]
            }
            Frame label="Map/Location" {
                Mapview item=DemoLocation height=10
            }
            
        }
        
    }
*/
}

 

Once again, please note that there are some items that still require some fine tune. For example, the way Yahoo shares the weather information has changed and I plan to use the Weather Binding from openHAB to get this to work - I left the code you see in the item file just as reference.

 

Presence Emulator

Strictly speaking, last feature of the project was developed in the PiIoT - DomPi 05: Ready for use Living Room and parents and kids´ bedrooms (lights control via the TV remote). That was "long" time ago and I am very happy to be able to deliver the next feature from the Project Status list image

 

The purpose of the Presence Emulator feature is in this first development to turn on and off the rooms´lights simulating that there is someone at home while we are away on vacations or for the weekend. This is key for us as we live in the basement and want to avoid "surprises" when returning home. I have developed a openHAB set of rules that govern this emulation. You can see the rule file below. Additionally I have added a new item to the item file (as per above), the Presence_Emulator, that allows the user to activate or deactivate the feature. Finally, I did add the item in the sitemap, I just added it to the main page to have it handy. If the main page starts to get busy I might move it out to other pages.

 

Rules in openHAB are quite straightforward to understand. They follow this structure:

  • import block
  • variable declarations
  • rule:
    • rule name. Just call it whatever you want, like "Initialize Presence Emulator"
    • when -> trigger conditions. There can be one or more conditions, depending on your needs
    • then   -> execution block

Don´t get me wrong, the structure is simple, but this can get really complex if your project requires it. To add the rule file, you just need to name it <name>.rules. You can add as many rule files as you need, however, those rules within the same file can share variables. In principle, I will try to keep the rules within the same file, meaning I will be updating this file in future posts.

 

Rule file

import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import org.joda.time.*

//Presence Emulator variables
var Number is_PresenceEmulator_on = 0    //Determines of the Presence Emulator is on or off
val java.util.Random rand = new java.util.Random    //object to get random numbers
//    variables to store the hour:min in minutes to actuate on the lamps
var Number l1on1=0
var Number l1off1=0
var Number l2on1=0
var Number l2off1=0
var Number l3on1=0
var Number l3off1=0

/**
 * Presence Emulator. 
 * Rules to 
 *     initialize activate and deactivate the Presence Emulator
 *  check if action is required
 *  launch a new timetable
 * 
 */
rule "Initialize Presence Emulator"
    when
        System started
    then
        sendCommand(Presence_Emulator, OFF)
        is_PresenceEmulator_on = 0 
end
rule "Presence Emulator to OFF"
    when
        Item Presence_Emulator changed from ON to OFF or
        Item Presence_Emulator received command OFF
    then
        is_PresenceEmulator_on = 0
end
rule "Presence Emulator to ON"
    when
        Item Presence_Emulator changed from OFF to ON or
        Item Presence_Emulator received command ON
    then
        if (is_PresenceEmulator_on == 0) {
            say("Creating a new Scheduler")
            //If the Presence Emulator was off, we activate it
            //If it was on (==1) we do nothing
            is_PresenceEmulator_on = 1
            //Init Lights 1 and 2. Only first cycle on-off
            var DateTime thoy_init  = parse(now.getYear() + "-" + now.getMonthOfYear() + "-" + now.getDayOfMonth() + "T00:00")
            var DateTime thoy         = parse(now.getYear() + "-" + now.getMonthOfYear() + "-" + now.getDayOfMonth() + "T00:00")
            //initialize first On to 19:00 plus random minutes between 0 and 30
                //Lamp 1 on
            l1on1 = 19*60 + rand.nextInt(31)
            thoy = thoy_init.plusMinutes(l1on1) 
            postUpdate(dtLampara_1_on1, new DateTimeType(thoy.toCalendar(null)))
                //Lamp 2 on
            l2on1 = 19*60 + rand.nextInt(31)
            thoy = thoy_init.plusMinutes(l2on1)
            postUpdate(dtLampara_2_on1, new DateTimeType(thoy.toCalendar(null)))
            
            //initialize first Off to 21:00 plus random minutes between 0 and 30
                //Lamp 1 off
            l1off1 = 21*60 + rand.nextInt(31)
            thoy = thoy_init.plusMinutes(l1off1)
            postUpdate(dtLampara_1_off1, new DateTimeType(thoy.toCalendar(null)))
                //Lamp 2 off
            l2off1 = 21*60 + rand.nextInt(31)
            thoy = thoy_init.plusMinutes(l2off1)
            postUpdate(dtLampara_2_off1, new DateTimeType(thoy.toCalendar(null)))
            
            //Init Light 3 only first cycle
                //Lamp 3 on. Random number from 20:30 to 21:14
            l3on1 = 20*60 + 30 + rand.nextInt(45)
            thoy = thoy_init.plusMinutes(l3on1)
            postUpdate(dtLampara_3_on1, new DateTimeType(thoy.toCalendar(null)))
                //Lamp 3 off. Random number from 22:45 to 23:30
            l3off1 = 22*60 + 45 + rand.nextInt(45)
            thoy = thoy_init.plusMinutes(l3off1)
            postUpdate(dtLampara_3_off1, new DateTimeType(thoy.toCalendar(null)))
        }

end

rule "Check if action is required in Presence Emulator"
    when
        Time cron "0 * * * * ?"                    //We check every minute if action is required
    then
        if (is_PresenceEmulator_on == 1) {        //We act only if the Presence Emulator is ON
            //Calculate current hour and minute and convert to minutes
            var Number tnow = now.getMinuteOfDay()    
            //say(tnow.toString)
            //say(l1on1.toString)
            //Compare minutes to turn Lamp 1, On cycle 1 with now. If equal, we actuate on the lamp
            //Same logic for rest of lamps and actions
            if (tnow==l1on1) {
                sendCommand(Lampara_1, "ON")
            } else if (tnow==l2on1) {
                sendCommand(Lampara_2, "ON")
            } else if (tnow==l3on1) {
                sendCommand(Lampara_3, "ON")
            } else if (tnow==l1off1) {
                sendCommand(Lampara_1, "OFF")
            } else if (tnow==l2off1) {
                sendCommand(Lampara_2, "OFF")
            } else if (tnow==l3off1) {
                sendCommand(Lampara_3, "OFF")
            } 
        }
end
rule "Presence Emulator get new Scheduler after midnight"
    when
        Time cron "1 0 0 * * ?"                    //We check every midnight on the 1st second
    then
        if (is_PresenceEmulator_on == 1) {        //We act only if the Presence Emulator is ON
            is_PresenceEmulator_on = 0            //We put it to zero, however with the sendCommand below, 
                                                //it will be reset to 1
            sendCommand(Presence_Emulator, ON)    //When forced ON, the rule above will create a new Schedule
        }
end
    
// Creates an item that stores the last update time of this item
rule "Records last weather update time"
when
  Item Weather_Temperature received update
then
  postUpdate(Weather_LastUpdate, new DateTimeType())
end

// This rule will be used to test Scale transformation service
rule "Compute humidex"
when
        Item Weather_Temperature changed or
    Item Weather_Humidity changed
then
    var Number T = Weather_Temperature.state as DecimalType
    var Number H = Weather_Humidity.state as DecimalType    
    var Number x = 7.5 * T/(237.7 + T)
    var Number e = 6.112 * Math::pow(10, x.doubleValue) * H/100
    var Number humidex = T + (new Double(5) / new Double(9)) * (e - 10)
    postUpdate(Weather_Humidex, humidex)
end

 

The key part is the rule called "Presence Emulator to ON", which is called when the feature is activated. Its first action is to create the scheduler for turning on and off the lights for the day of today. In this first development I have just added 1 on cycle and 1 off cycle per light. The light in the living room has different hours than the other two for turning on and off. To make it more real, I have randomized all of the times, so that each day the lights turn on and off at different hours.

 

This approach is quite basic and in future developments I plan to add: automatic addition of another cycle for Winter (it will differentiate between Summer and Winter since where I live there is big time differences) and I will add some random cycles (from time to time, to turn on/off the lights more times per day). Not sure if I will be able to make it within the PiIoT challenge as I will need to focus on adding more features. In any case, in the items file, I have already added a placeholder for a second cycle for all of the lights.

 

Finally, a cool feature from openHAB is that you can hide/unhide some items in the User Interface by adding the "visibility" label. In the screenshot above of openHAB for the main page, you can see the buttons to activate/deactivate the Presence Emulator. If it is "On", openHAB will display an additional clickable line that brings you to the scheduler to check the hours when the lights will be turned on and off. See the screenshots below.

 

image

image

image

This is all for this post! As noted above, please take these files as interim and forgive any bad-coding - I´m just discovering this as we go!

 

Nodes´ Dashboard

image

  • Sign in to reply
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube