[90] MQTT Support

Use this forum to discuss possible implementation of a new feature before opening a ticket.
A developer shall edit the topic title with "[xxx]" where xxx is the id of the accompanying tracker id.
Duplicate posts about the same id. +1 posts are not allowed.

Moderators: leecollings, remb0

maxtrash
Posts: 106
Joined: Tuesday 06 August 2013 1:31
Target OS: -
Domoticz version:
Contact:

Re: MQTT Support

Post by maxtrash »

again some code example for domoticz.js, continuing where you left
it will support different types of messages (json or normal, and using idx or dtype and dsubtype), once you see the source I think it makes sense. Feedback appreciated.
I can definately see the flexibility that JSON provides.

I'm trying to make it even more userfriendly by adding some option to the json for passing SensorType:
{
"data":[{
"SensorType":"Temperature",
"svalue":"21.1"
}]
}
this hasn't finished yet because it's quite a lot of work and also Domoticz isn't always consistently behaving here. Support here would be welcome.

Code: Select all

var	Domoticz_HID = '3'; 										//Hardware ID of dummy in Domoticz
	Domoticz_IP = '127.0.0.1'; 									//IP address of Domoticz (127.0.0.1 for same machine)
	Domoticz_Port = '8080';										//Port of Domoticz

var mqtt = require('mqtt');
var url = require('url');
var http = require('http');
var request = require('request');

client = mqtt.createClient(1883, 'localhost');

// the lua-script in domoticz pushes all events to port 5001. This function will republish them on the mqtt bus
http.createServer(function (req, res) {
	res.writeHead(200, {'Content-Type': 'text/plain'});
	res.end('Response from Node.js \n');
	
	console.log('publish: '+'/events/domoticz'+url.parse(req.url).pathname, url.parse(req.url).query);
	client.publish('/events/domoticz'+url.parse(req.url).pathname, url.parse(req.url).query);	
}).listen(5001, 'localhost');

/* here we subscribe to topic /actions/domoticz in mqtt. Parsable messages which are published here will be sent to domoticz
Example data 1 (JSON, idx-value):
mosquitto_pub -t /actions/domoticz/xyz -m '
{
   "data":[{
      "idx":247,
      "svalue":"21.1;70%"
   }]
}'

Example data 2 (JSON, did/dunit/dtype/dsubtype):
{
   "data":[{
      "dunit":1,
	  "dtype":80,
	  "dsubtype":9,
      "svalue":"21.1"
   }]
}
(for dtype and dsubtype please refer to http://sourceforge.net/p/domoticz/code/HEAD/tree/domoticz/main/RFXtrx.h
e.g. 80,9 = temperature, 82,10 = temperature+humidity, 32, 1 = shuttercontact)

Example data 3 (Normal, idx-value):
mosquitto_pub -t /actions/domoticz/247 -m 21.1

Example data 4 (JSON, SensorType):
{
   "data":[{
      "SensorType":"Temperature",
      "svalue":"21.1"
   }]
}
*/
client.subscribe('/actions/domoticz/#');
client.on('message', function (topic, message) {
  console.log('Received: '+ topic + ' ' + message);
  var url = 'http://'+Domoticz_IP+':'+Domoticz_Port;
  var svalue = 0;
  var idx = 0;
  var nvalue = 0;
  var dunit = 1;  	// default 
  var dtype = 80;   // default = temperature device
  var dsubtype = 9;
      
  try {
    var payload = JSON.parse(message);  
    if (payload.data !=  null) {
	  console.log('JSON Payload');
	  for (var i = payload.data.length - 1; i >= 0; i--) {
      var data = payload.data[i];
      if(data.nvalue) {
        nvalue = data.nvalue;
      }
      if(data.svalue) {
        svalue = data.svalue;
      }
	  if(data.dunit) {
	    dunit = data.dunit;
	  }
	  if(data.dsubtype) {
	    dsubtype = data.dsubtype;
	  }
	  //use idx if found, otherwise use hid/did/dunit/dtype/dsubtype style of interfacing with domoticz.
	  if(data.idx) {
        idx = data.idx;
		url = url + "/json.htm?type=command&param=udevice&idx="+idx+
			"&nvalue="+nvalue+"&svalue="+svalue;

      }
	  else {
	    if (data.SensorType) {
			switch (data.SensorType.toLowerCase()) {
			case "temperature":
				dtype = 80;
				dsubtype = 1;
				break;
			case "humidity":
				dtype = 81;
				dsubtype = 1;
				break;
			case "temp+hum":
				dtype = 82;
				dsubtype = 1;
				svalue = svalue + ';0';  // don't know why this is needed
				// also for no apparent reason the &did cannot be a random string so it will end up like 0000
				break;
			case "temp+hum+baro":
				dtype = 84;
				dsubtype = 1;
				// unclear what the svalue should look like ???
				break;
			case "rain":
				dtype = 85;
				dsubtype = 3;
				break;
			case "wind":
				dtype = 86;
				dsubtype = 1;
				// 0;N;0;0;0;0 ???
				break;
			case "uv":
				dtype = 87;
				dsubtype = 1;
				// 0.0 UVI
				break;
				case "uv":
			case "electricity":
				dtype = 248;
				dsubtype = 1;
				// 0.0 UVI
			case "gas":
			    dtype = 113;
				dsubtype = 0;
				break;
			case "shuttercontact":
				dtype = 32;
				dsubtype = 1;
				break;
			default:
				console.log('Warning: no matching SensorType. Default used');
				break;				
			};
		};
		url = url + "/json.htm?type=command&param=udevice&hid="+Domoticz_HID+"&did="+topic+"&dunit="+dunit+"&dtype="+dtype+"&dsubtype="+dsubtype+
			"&nvalue="+nvalue+"&svalue="+svalue;  
	  };
      
	  request(url, function(error, response, body){
        console.log("Sending request");
        console.log(url);
        console.log(error);
      //  console.log(response);
        console.log(body);
      });
	  }
    }
	else {
		console.log('Normal Payload');
	  
		var parts = topic.split("/");
		idx = parts.pop();
		svalue = message;
		
		url = url + "/json.htm?type=command&param=udevice&idx="+idx+"&nvalue=0&svalue="+svalue;
		request(url, function(error, response, body){
			console.log("Sending request");
			console.log(url);
			console.log(error);
		//	console.log(response);
			console.log(body);
		  });
		
	};
  }
  catch(e) {
    console.log("Could not parse payload");
    console.log(e);
  }
});
sebas
Posts: 117
Joined: Tuesday 17 September 2013 7:46
Target OS: Linux
Domoticz version:
Location: Deventer, The Netherlands
Contact:

Re: MQTT Support

Post by sebas »

andriej wrote:The best way I see the cooperation between Domoticz and multiple Arduino sensors is MQTT on i.e. Arduino MEGA and nodes (i.e. Nanos).
That's the setup I'm currently using, so Arduino Mega is connected to LAN via Ethernet and it uses RF24Network library (2.4GHz rx/tx) to get values from sensors (up to >2000 possible nodes).

The only problem I see is that RF24Network can send/receive only 24bytes of data in one packet, so I guess it's not enough for longer commands/data for MQTT?
I can't wait to see your examples/sketches. I'll try to help in this thread, as it's the only way I can get my remotes, sensors and weather station connected to Domoticz (RFXCom currently doesn't support it, so I'm left with unusable device...)

Regards

--edit:
Did some googe'ing for RF24 and MQTT to implement.
Found this library (for Raspberry): https://github.com/mannkind/Ripcord
May be a hint.
But still - better use would come from "RF24Network" library on MEGA with proper rx/tx of MQTT.
The 24 bytes won't be enough for direct MQTT communication I think as messages can easily get bigger than that. The Mega can do the MQTT communication and then send only the commands to the Nano's. In one MQTT message you can put commands for several Nano's (when running a scene for example). The Mega will distribute the commands over RF24.
RF24, is that using the Xbee boards?
maxtrash wrote:again some code example for domoticz.js, continuing where you left
it will support different types of messages (json or normal, and using idx or dtype and dsubtype), once you see the source I think it makes sense. Feedback appreciated.
I can definately see the flexibility that JSON provides.

I'm trying to make it even more userfriendly by adding some option to the json for passing SensorType:
{
"data":[{
"SensorType":"Temperature",
"svalue":"21.1"
}]
}
this hasn't finished yet because it's quite a lot of work and also Domoticz isn't always consistently behaving here. Support here would be welcome.
Great work! You're combining the scripts to push data to and from Domoticz enabling 2-way communication, correct?

Where do I find the dtype and dsubtype in http://sourceforge.net/p/domoticz/code/ ... n/RFXtrx.h?

Coming weekend I'll have some time to work on the Arduino side of things. I've got some sensors and relays that I can use.
maxtrash
Posts: 106
Joined: Tuesday 06 August 2013 1:31
Target OS: -
Domoticz version:
Contact:

Re: MQTT Support

Post by maxtrash »

yes, two-way communication is implemented.

When using JSON-style two ways are supported. Either using idx, in case the device is already created in Domoticz or dtype/subdtype to push new devices to the unused tab of domoticz. In the RFXtrx.h file the values are in hex and called something like pTypeTEMP (0x50) or sTypeTEMP1 (1).

When using normal style messages, only the idx-style is supported. The last part of the topic name is considered to be the idx number, so publishing to /actions/domoticz/a/b/c/123 will update the device with idx-123.

In the weekend I think I'm going to experiment a little with pvoutput support (e.g. create a pvoutput.js module that will publish events to /actions/domoticz/solarpanels/electricity which will then be pushed to domoticz by domoticz.js
Last edited by maxtrash on Friday 03 January 2014 12:48, edited 2 times in total.
andriej
Posts: 46
Joined: Tuesday 10 December 2013 22:27
Target OS: Linux
Domoticz version: beta
Contact:

Re: MQTT Support

Post by andriej »

sebas wrote:The 24 bytes won't be enough for direct MQTT communication I think as messages can easily get bigger than that. The Mega can do the MQTT communication and then send only the commands to the Nano's. In one MQTT message you can put commands for several Nano's (when running a scene for example). The Mega will distribute the commands over RF24.
RF24, is that using the Xbee boards?
No, it's much cheaper than Xbee - that's why I'm using it in my setup.
I agree that some kind of protocol on MEGA should do the commands.
My programming skills aren't exactly that good to write it from the beginning tho. I hope there will be a sketch to work on later on, with MQTT support so I'll try too look how to put up Mega-Nodes communication via RF24.
Orange Pi (@ Debian) / MySensors Serial / GPIO / Custom Serial 433 MHz Transmitter (for plug switches and livolo wall switches) / JSON&Bash API scripts
sebas
Posts: 117
Joined: Tuesday 17 September 2013 7:46
Target OS: Linux
Domoticz version:
Location: Deventer, The Netherlands
Contact:

Re: MQTT Support

Post by sebas »

Based on some tutorials I found online I made an Arduino sketch to test MQTT. My Arduino Mega is now publishing data to the MQTT broker on my Raspberry PI.
It's currently a dummy value as I don't have any sensors connected yet.

You will need this Arduino library: http://knolleary.net/arduino-client-for-mqtt/. This sketch is based on using an Ethernetshield.

May be of help to some:

Code: Select all

#include <SPI.h>
#include <PubSubClient.h>
#include <Ethernet.h>


#define  DEVICE_ID  "Arduino"

#define MQTT_SERVER "192.168.1.3"

byte MAC_ADDRESS[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x31, 0xB8 };

EthernetClient ethernetClient;
PubSubClient sensorClient(MQTT_SERVER, 1883, sensorCallback, ethernetClient);

char clientID[50];
char topic[50];
char msg[50];


// variables to store current time (millis)
// used to control frequency of sensor reporting
unsigned long now = 0;
unsigned long sentTime = 0;
#define REPORTING_INTERVAL_MS  1000


void setup()
{
  // init serial link for debugging
  Serial.begin(9600);
  Serial.println("Starting...");

  // initialize ethernet library  
  if (Ethernet.begin(MAC_ADDRESS) == 0)
  {
      Serial.println("Failed to configure Ethernet using DHCP");
      return;
  }

  String clientIDStr = "Arduino";
  clientIDStr.concat(DEVICE_ID);
  clientIDStr.toCharArray(clientID, clientIDStr.length()+1);
   
  String topicStr = "/Arduino/";
  topicStr.concat(DEVICE_ID);
  topicStr.toCharArray(topic, topicStr.length()+1);
 
  
}

void loop()
{
  now = millis();
  
  if (!sensorClient.connected()) {
    sensorClient.connect(clientID);
  }
  
  if ((now - sentTime) > REPORTING_INTERVAL_MS) {
    sentTime = now;
     
      
    // build MQTT message payload
    String dataMsg = "{\"t\":";
    dataMsg.concat(21);
    dataMsg.concat("}");
    dataMsg.toCharArray(msg, dataMsg.length()+1);
    sensorClient.publish(topic, msg);     
    Serial.println(dataMsg);
  }
  sensorClient.loop();
  
}

// handles message arrived on subscribed topic(s)
// in this application we do not subscribe so this is a dummy method
void sensorCallback(char* topic, byte* payload, unsigned int length) { }

sebas
Posts: 117
Joined: Tuesday 17 September 2013 7:46
Target OS: Linux
Domoticz version:
Location: Deventer, The Netherlands
Contact:

Re: MQTT Support

Post by sebas »

Got the Arduino Mega and Domoticz connected. The Arduino is updating a temperature and humidity sensor (Test meter) in Domoticz nicely.
arduino-temp-domoticz.jpg
arduino-temp-domoticz.jpg (163.44 KiB) Viewed 9857 times
The NodeJS server file

Code: Select all

var IP = '127.0.0.1';                            //IP address of Domoticz (127.0.0.1 for same machine)
var Port = '8080';                              //Port of Domoticz

var sys = require('sys');
var net = require('net');
var request = require('request');
var mqtt = require('mqtt')

client = mqtt.createClient(1883, 'localhost');

var toString = function(text) {
   return text + '';
};

client.subscribe('/Arduino/#');
client.publish('/Arduino/', 'This is Domoticz');

client.on('message', function (topic, message) {
  
  try {
    var payload = JSON.parse(message);  
    var url = 'http://'+IP+':'+Port;
    var svalue = 0;
    var idx = 0;
    var nvalue = 0;

    for (var i = payload.data.length - 1; i >= 0; i--) {
      var data = payload.data[i];
      if(data.idx) {
        idx = data.idx;
      }
      if(data.nvalue) {
        nvalue = data.nvalue;
      }
      if(data.svalue) {
        svalue = data.svalue;
      }
      url = url + "/json.htm?type=command&param=udevice&idx="+idx+"&nvalue="+nvalue+"&svalue="+svalue;
      request(url, function(error, response, body){
      });
    };
  }
  catch(e) {
    console.log("Could not parse JSON.");
    console.log(e);
  }

});
The Arduino sketch

Code: Select all

#include <dht11.h>
#include <SPI.h>
#include <PubSubClient.h>
#include <Ethernet.h>


// Temp + Humidity sensor configuration
dht11 DHT11;
#define DHT11PIN A0

//MQTT configuration
#define  DEVICE_ID  "Mega"
#define MQTT_SERVER "192.168.1.3"

//Ethernet shield
byte MAC_ADDRESS[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x31, 0xB8 };
EthernetClient ethernetClient;

//MQTT initialisation
PubSubClient sensorClient(MQTT_SERVER, 1883, sensorCallback, ethernetClient);
char clientID[50];
char topic[50];
char msg[50];


// variables to store current time (millis)
// used to control frequency of sensor reporting
unsigned long now = 0;
unsigned long sentTime = 0;
#define REPORTING_INTERVAL_MS  10000

//Dewpoint calculation
double dewPoint(double celsius, double humidity)
{
	// (1) Saturation Vapor Pressure = ESGG(T)
	double RATIO = 373.15 / (273.15 + celsius);
	double RHS = -7.90298 * (RATIO - 1);
	RHS += 5.02808 * log10(RATIO);
	RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1/RATIO ))) - 1) ;
	RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1) ;
	RHS += log10(1013.246);

        // factor -3 is to adjust units - Vapor Pressure SVP * humidity
	double VP = pow(10, RHS - 3) * humidity;

        // (2) DEWPOINT = F(Vapor Pressure)
	double T = log(VP/0.61078);   // temp var
	return (241.88 * T) / (17.558 - T);
}


void setup()
{
  // init serial link for debugging
  Serial.begin(9600);
  Serial.println("Starting...");

  // initialize ethernet library  
  if (Ethernet.begin(MAC_ADDRESS) == 0)
  {
      Serial.println("Failed to configure Ethernet using DHCP");
      return;
  }
  
  //Setup MQTT connection
  String clientIDStr = "Arduino-";
  clientIDStr.concat(DEVICE_ID);
  clientIDStr.toCharArray(clientID, clientIDStr.length()+1);
   
  String topicStr = "/Arduino/";
  topicStr.concat(DEVICE_ID);
  topicStr.toCharArray(topic, topicStr.length()+1);
 
  
}

void loop()
{
  now = millis();
  
  //Connect to MQTT broker
  if (!sensorClient.connected()) {
    sensorClient.connect(clientID);
  }
  
  if ((now - sentTime) > REPORTING_INTERVAL_MS) {
    sentTime = now;
    
    //Read temp & humidity from sensor
    int chk = DHT11.read(DHT11PIN);
    Serial.print("Read sensor: ");
    switch (chk)
    {
      case DHTLIB_OK: 
  		Serial.println("OK"); 
  		break;
      case DHTLIB_ERROR_CHECKSUM: 
  		Serial.println("Checksum error"); 
  		break;
      case DHTLIB_ERROR_TIMEOUT: 
  		Serial.println("Time out error"); 
  		break;
      default: 
  		Serial.println("Unknown error"); 
  		break;
    }
     
    //Convert temp and humidity into something we can use
    //Calculate dew point
    char temp[10];
    char hum[10];
    char dew[10];
    
    dtostrf((float)DHT11.humidity,1,2,hum);
    dtostrf((float)DHT11.temperature,1,2,temp);
    dtostrf(dewPoint(DHT11.temperature, DHT11.humidity),1,2,dew);
    
    Serial.print("Humidity (%): ");
    Serial.println(hum);
  
    Serial.print("Temperature (°C): ");
    Serial.println(temp);
    Serial.print("Dew Point (°C): ");
    Serial.println(dew);
      
    // build MQTT message payload
    String dataMsg = "{\"data\": [{\"idx\": 247,";
    dataMsg.concat("\"svalue\": \"");
    dataMsg.concat(temp);
    dataMsg.concat(";");
    dataMsg.concat(hum);
    dataMsg.concat(";");
    dataMsg.concat(dew);
    dataMsg.concat("\"}]}");
    dataMsg.toCharArray(msg, dataMsg.length()+1);
    
    //Publish payload to MQTT broker
    sensorClient.publish(topic, msg);     
    Serial.println(dataMsg);
  }
  sensorClient.loop();
}

// handles message arrived on subscribed topic(s)
// in this application we do not subscribe so this is a dummy method
void sensorCallback(char* topic, byte* payload, unsigned int length) { }
The hardware I use for the Arduino is a Arduino Mega 2560 with the Arduino Ethernetshield and a DHT11 temperature + humidity sensor. The dew point is calculated by the Arduino.
Every 10 seconds the Arduino takes a reading of the sensor and publishes that to the MQTT broker running on the RaspberryPI that also runs Domoticz. The NodeJS server is subscribed to the topic th Arduino publishes to and forwards the message to the Domoticz JSON API.

Next steps for me will be to add the code from maxtrash for de dtype etc. use instead of the idx and to finally figure out how that works and to enable two way communication.
maxtrash
Posts: 106
Joined: Tuesday 06 August 2013 1:31
Target OS: -
Domoticz version:
Contact:

Re: MQTT Support

Post by maxtrash »

I have started experimenting a little with pvoutput support. So my setup is to have a server.js file, which contains require('mqtt') and require('pvoutput'). The pvoutput modules publishes messages to MQTT which are sent to domoticz by mqtt.js.
see attachement
Attachments
nodejs-domoticz.zip
(2.76 KiB) Downloaded 236 times
andriej
Posts: 46
Joined: Tuesday 10 December 2013 22:27
Target OS: Linux
Domoticz version: beta
Contact:

Re: MQTT Support

Post by andriej »

Well I had a look at my current installation of arduino's nodes and MEGA and I can't switch eveything to MQTT just yet. I'd like to transfer messages from nodes already in MQTT format, but ATM it's not possible (RF24Network can carry only 24bytes in one message).
For everything else I'm using URL to put data to Domoticz. But I will be watching this thread or at least try to understand how it works. :-)
Orange Pi (@ Debian) / MySensors Serial / GPIO / Custom Serial 433 MHz Transmitter (for plug switches and livolo wall switches) / JSON&Bash API scripts
andriej
Posts: 46
Joined: Tuesday 10 December 2013 22:27
Target OS: Linux
Domoticz version: beta
Contact:

Re: MQTT Support

Post by andriej »

I'm working even further for the "MEGA Gateway", but at current moment MQTT support integraded in Domoticz would be the best option to be.
Asking broker for current statuses and updating them via Arduino is the only way we can get it to work I think...

gizmocuz?
Orange Pi (@ Debian) / MySensors Serial / GPIO / Custom Serial 433 MHz Transmitter (for plug switches and livolo wall switches) / JSON&Bash API scripts
kylegordon
Posts: 26
Joined: Thursday 31 October 2013 23:58
Target OS: Linux
Domoticz version: Trunk
Contact:

Re: MQTT Support

Post by kylegordon »

Here's an interesting concept just published the other day http://www.housahedron.co.uk/2014/01/06 ... on-so-far/

In short, use a Jeenode/RFM12B/Arduino/etc hooked up to a server running a broker, and they act as a socket manager for the remote devices to wake up and use periodically. Radically different from the way I do it (TinyTX/Jeenode -> Jeelink/Jeenode -> Serial -> MQTT), but definitely food for thought.
1 x HP DL380 & KVM
1 x RPI
1 x RFXtrx433 V78
11 x LWRF Switches
1 x LIFX
2 x Echo Dots
6 x Byron PIRs
2 x Nexa PIRs
2 x Kodi
2 x ESP8266 MQTT
1 x GPIO/MQTT PIR
1 x GPIO(PWM)/MQTT LEDs
1 x GPIO(SPI)/MQTT LPD6803 LEDs
Lots of Node-Red glue
andriej
Posts: 46
Joined: Tuesday 10 December 2013 22:27
Target OS: Linux
Domoticz version: beta
Contact:

Re: MQTT Support

Post by andriej »

I read more on MQTT and I decided I will move my current installation of MEGA and nodes to it.

Could someone please put-up a simple how-to on Domoticz Wiki how to integrate it?
I'd like to have MQTT as a 'bus' of communication between arduino and domoticz itself.
So, in example, if I press switch 'off' in domoticz, mqtt should be updated and arduino picks it up and sends 433mhz/2.4ghz properly.
I can see some example code's in thread but have no clue where to put them, so simple 1-2-3 steps would make a difference.

Now I'm looking for a good way of managing the threads, so all devices I'm interested in will be reported properly:
- RF433 MHz outlets controller (device 'on'/'off'/'change')
- temp/hum sensors
- light sensors
- pir sensors
- contact (open/close)
- energy meter (kwh counter + current usage)
- rest :-) I can imagine.

I will also have to decide to rearrange the payload for RF24 so it can handle more data in these 24bytes and be more scalable (so maybe one day I put it on the web).

Do you have any ideas how to arrange these topics properly?
Or it's just scalable in future and always possible to migrate?
Current idea is:
/home/{node}/{device|sensor}/{status|value}
Orange Pi (@ Debian) / MySensors Serial / GPIO / Custom Serial 433 MHz Transmitter (for plug switches and livolo wall switches) / JSON&Bash API scripts
maxtrash
Posts: 106
Joined: Tuesday 06 August 2013 1:31
Target OS: -
Domoticz version:
Contact:

Re: MQTT Support

Post by maxtrash »

I'm trying to make some sort of a "device-driver" for Domoticz. I think it would be ideal if we all use the same structure in publishing for all our connections (e.g. arduino). Personally I'm working on PVOUTPUT support and mqttitude).
They publish topics like /actions/domoticz/pvoutput with payload '{"SensorType":"Electricity","Svalue":"xxxx"}'
and then the domoticz-driver takes care of that.

I would very much welcome anyones suggestions and changes, I'm currently refactoring the code a little and will post it later today
andriej
Posts: 46
Joined: Tuesday 10 December 2013 22:27
Target OS: Linux
Domoticz version: beta
Contact:

Re: MQTT Support

Post by andriej »

maxtrash wrote:I'm trying to make some sort of a "device-driver" for Domoticz. I think it would be ideal if we all use the same structure in publishing for all our connections (e.g. arduino). Personally I'm working on PVOUTPUT support and mqttitude).
They publish topics like /actions/domoticz/pvoutput with payload '{"SensorType":"Electricity","Svalue":"xxxx"}'
and then the domoticz-driver takes care of that.
Reporting from Arduino in JSON-format, am I understading it right?
I'm open to everything as long as I will be able not only to report sensors, but also change values of switches and give other commands.
That's what I don't like in current situation of reporting values to Domoticz - I have to look for types, subtypes, try to guess them, test them instead of creating sensor and defining pValue/sValue/type etc. (and still the sensor name isn't true as it could be 'andriej's sensor' ;-))

Anyway - I would like to write sketch for mega which will allow to test out i.e. openHAB (which uses MQTT also) without having to rebuild everything.
In my opinion it would be better to stick to whatever there is, as long as it's interoperable with other systems or easy to maintain that.

--Edit:
I've just checked what the pvoutput is.. well it looks like it's only for energy meters, which is not what I was thinking about.
I'm looking at it more from the top, to maintain "standard" for every situation - from sensors to switches.
Orange Pi (@ Debian) / MySensors Serial / GPIO / Custom Serial 433 MHz Transmitter (for plug switches and livolo wall switches) / JSON&Bash API scripts
maxtrash
Posts: 106
Joined: Tuesday 06 August 2013 1:31
Target OS: -
Domoticz version:
Contact:

Re: MQTT Support

Post by maxtrash »

andriej wrote:
maxtrash wrote:I'm trying to make some sort of a "device-driver" for Domoticz. I think it would be ideal if we all use the same structure in publishing for all our connections (e.g. arduino). Personally I'm working on PVOUTPUT support and mqttitude).
They publish topics like /actions/domoticz/pvoutput with payload '{"SensorType":"Electricity","Svalue":"xxxx"}'
and then the domoticz-driver takes care of that.
Reporting from Arduino in JSON-format, am I understading it right?
yes, but directly using idx is also possible although I would not recommend that.
I'm open to everything as long as I will be able not only to report sensors, but also change values of switches and give other commands.
That's what I don't like in current situation of reporting values to Domoticz - I have to look for types, subtypes, try to guess them, test them instead of creating sensor and defining pValue/sValue/type etc. (and still the sensor name isn't true as it could be 'andriej's sensor' ;-))
I also don't like looking up types etc. but since I'm developing the script I have to... But for the "end" user it can be hidden behind "SensorType". And we can expand the supported SensorTypes as much as we want, including switches, as long as it's supported by domoticz' json interface.
Anyway - I would like to write sketch for mega which will allow to test out i.e. openHAB (which uses MQTT also) without having to rebuild everything.
In my opinion it would be better to stick to whatever there is, as long as it's interoperable with other systems or easy to maintain that.

--Edit:
I've just checked what the pvoutput is.. well it looks like it's only for energy meters, which is not what I was thinking about.
I'm looking at it more from the top, to maintain "standard" for every situation - from sensors to switches.
the pvoutput-module is just a proof of concept using one SensorType in Domoticz. Have a look at this code and the remarks in it.

Code: Select all

/*
this script will act as a device-driver for Domoticz. 
Events in Domoticz are published to topics beneath /events/domoticz/# 
Actions that Domoticz should perform can be triggered by publishing to /actions/domoticz/#

See also topic http://www.domoticz.com/forum/viewtopic.php?f=5&t=838
*/

var	Domoticz_HID = '3'; 										//Hardware ID of dummy in Domoticz
	Domoticz_IP = '127.0.0.1'; 									//IP address of Domoticz (127.0.0.1 for same machine)
	Domoticz_Port = '8080';										//Port of Domoticz

var mqtt = require('mqtt');
var url = require('url');
var http = require('http');
var request = require('request');

client = mqtt.createClient(1883, 'localhost');

// the lua-script in domoticz pushes all events to port 5001. This function will publish them on the mqtt bus
http.createServer(function (req, res) {
	res.writeHead(200, {'Content-Type': 'text/plain'});
	res.end('Response from Node.js \n');
	
	console.log('publish: '+'/events/domoticz'+url.parse(req.url).pathname, url.parse(req.url).query);
	client.publish('/events/domoticz'+url.parse(req.url).pathname, url.parse(req.url).query);	
}).listen(5001, 'localhost');

/* here we subscribe to topic /actions/domoticz in mqtt. Parseble messages which are published here will be sent to Domoticz
open a new SSH window and type 'mosquitto_sub -v -t /#' to test

Example 1 (JSON, SensorType):
test: 			mosquitto_pub -t /actions/domoticz/xyz -m '{"SensorType":"Temperature","svalue":"21.1"}'
intended use:	if you want new devices to be created in the unused tab automatically. 
explanation: 	using a lookup table, the SensorType will be mapped to certain combination of dtype and dsubtype. I'm trying to use as much as possible the names and types as used for virtual sensors in the domoticz GUI.

Example 2 (JSON, idx-value):
test: 			mosquitto_pub -t /actions/domoticz/xyz -m '{"idx":247,"svalue":"21.1;70%"}'
intended use: 	if you (or something else) already created the sensor in Domoticz it can be referred by it's idx-number

Example 2 (JSON, did/dunit/dtype/dsubtype):
test:			mosquitto_pub -t /actions/domoticz/xyz -m '{"dunit":1,"dtype":80,"dsubtype":9,"svalue":"21.1"}'
intended use: 	if you want to use your own dtype/dsubtype instead of the predefined ones in SensorType or dunit other than 1

(for dtype and dsubtype please refer to http://sourceforge.net/p/domoticz/code/HEAD/tree/domoticz/main/RFXtrx.h
e.g. 80,9 = temperature, 82,10 = temperature+humidity, 32, 1 = shuttercontact)

Example 4 (Normal, idx-value):
test:			mosquitto_pub -t /actions/domoticz/247 -m 21.1
intended use: 	if you find JSON a bit complicated
explanation:	last number in topic should be idx-number. Payload is svalue.
*/

client.subscribe('/actions/domoticz/#');
client.on('message', function (topic, message) {
  console.log('Received: '+ topic + ' ' + message);
  var url = 'http://'+Domoticz_IP+':'+Domoticz_Port;
  
  var svalue = 0;
  var nvalue = 0;
  var idx = 0;
  var dunit = 1;  	// default 
  var dtype = 80;   // default = temperature device
  var dsubtype = 9;
      
  try {
    var payload = JSON.parse(message);  
    
	if (payload !=  null) {
	  console.log('JSON Payload');
	  if(payload.nvalue) {
        nvalue = payload.nvalue;
      }
      if(payload.svalue) {
        svalue = payload.svalue;
      }
	  if(payload.dunit) {
	    dunit = payload.dunit;
	  }
	  if(payload.dsubtype) {
	    dsubtype = payload.dsubtype;
	  }
	  //use idx if found, otherwise use hid/did/dunit/dtype/dsubtype style of interfacing with domoticz.
	  if(payload.idx) {
        idx = payload.idx;
		url = url + "/json.htm?type=command&param=udevice&idx="+idx+
			"&nvalue="+nvalue+"&svalue="+svalue;

      }
	  else {
	    if (payload.SensorType) {
			switch (payload.SensorType.toLowerCase()) {
			case "temperature":
				dtype = 80;
				dsubtype = 1;
				break;
			case "humidity":
				dtype = 81;
				dsubtype = 1;
				break;
			case "temp+hum":
				dtype = 82;
				dsubtype = 1;
				svalue = svalue + ';0';  // don't know why this is needed
				// also for no apparent reason the &did cannot be a random string so it will end up like 0000
				break;
			case "temp+hum+baro":
				dtype = 84;
				dsubtype = 1;
				// unclear what the svalue should look like ???
				break;
			case "rain":
				dtype = 85;
				dsubtype = 3;
				break;
			case "wind":
				dtype = 86;
				dsubtype = 1;
				// 0;N;0;0;0;0 ???
				break;
			case "uv":
				dtype = 87;
				dsubtype = 1;
				// 0.0 UVI
				break;
			case "electricity":
				dtype = 250;
				dsubtype = 1;
				break;
			case "gas":
			    dtype = 251;
				dsubtype = 0;
				break;
			case "shuttercontact":
				dtype = 32;
				dsubtype = 1;
				break;
			case "counter":
				dtype = 113;
				dsubtype = 0;
				break;
			default:
				console.log('Warning: no matching SensorType. Default used');
				break;				
			};
		};
		url = url + "/json.htm?type=command&param=udevice&hid="+Domoticz_HID+"&did="+topic+"&dunit="+dunit+"&dtype="+dtype+"&dsubtype="+dsubtype+
			"&nvalue="+nvalue+"&svalue="+svalue;  
	  };
    }
    else {
		console.log('Normal Payload');
	  
		var parts = topic.split("/");
		idx = parts.pop();
		svalue = message;
		
		url = url + "/json.htm?type=command&param=udevice&idx="+idx+"&nvalue=0&svalue="+svalue;
	};
	request(url, function(error, response, body){
	console.log("Sending request");
	console.log(url);
	console.log(error);
	//	console.log(response);
	console.log(body);
  });

  }
  catch(e) {
    console.log("Could not parse payload");
    console.log(e);
  }
});
open a new SSH window and type 'mosquitto_sub -v -t /#' to test

Example 1 (JSON, SensorType):
test: mosquitto_pub -t /actions/domoticz/xyz -m '{"SensorType":"Temperature","svalue":"21.1"}'
intended use: if you want new devices to be created in the unused tab automatically.
explanation: using a lookup table, the SensorType will be mapped to certain combination of dtype and dsubtype. I'm trying to use as much as possible the names and types as used for virtual sensors in the domoticz GUI.

Example 2 (JSON, idx-value):
test: mosquitto_pub -t /actions/domoticz/xyz -m '{"idx":247,"svalue":"21.1;70%"}'
intended use: if you (or something else) already created the sensor in Domoticz it can be referred by it's idx-number

Example 2 (JSON, did/dunit/dtype/dsubtype):
test: mosquitto_pub -t /actions/domoticz/xyz -m '{"dunit":1,"dtype":80,"dsubtype":9,"svalue":"21.1"}'
intended use: if you want to use your own dtype/dsubtype instead of the predefined ones in SensorType or dunit other than 1

(for dtype and dsubtype please refer to http://sourceforge.net/p/domoticz/code/ ... n/RFXtrx.h
e.g. 80,9 = temperature, 82,10 = temperature+humidity, 32, 1 = shuttercontact)

Example 4 (Normal, idx-value):
test: mosquitto_pub -t /actions/domoticz/247 -m 21.1
intended use: if you find JSON a bit complicated
explanation: last number in topic should be idx-number. Payload is svalue.
andriej
Posts: 46
Joined: Tuesday 10 December 2013 22:27
Target OS: Linux
Domoticz version: beta
Contact:

Re: MQTT Support

Post by andriej »

Great, I will try it out then.
I hope to see the little how-to help for setting up your module/driver when it's ready (the JSON part, mosquitto I already have up and running).

In spare time I'll start moving my code to MQTT.
Orange Pi (@ Debian) / MySensors Serial / GPIO / Custom Serial 433 MHz Transmitter (for plug switches and livolo wall switches) / JSON&Bash API scripts
maxtrash
Posts: 106
Joined: Tuesday 06 August 2013 1:31
Target OS: -
Domoticz version:
Contact:

Re: MQTT Support

Post by maxtrash »

the pvoutput is example is not that interesting, especially for people without solar panels. This one is way more cool: http://mqttitude.org/

how the scripts work:
- the mqttitude app will publish the location of the device to a /mqttitude on your broker (with payload something like this: {"_type": "location", "lat": "41.5223365", "lon": "4.0491054", "tst": "1389194174", "acc": "18.68m", "alt": "0.0"})
- mqttitude.js will then parse that message and republish it to /actions/domoticz/mqttitude (with payload like this: {"SensorType":"counter","svalue":"361.5km"})
- domoticz.js subscribes to /actions/domoticz/# and pushes everyting to domoticz

required:
- mosquitto broker on raspberry
- install mqttitude on android or iPhone
- put a server.js like this on the raspberry and start it using "nodemon server.js":

Code: Select all

// main nodejs script

var domoticz = require('./domoticz.js');
var mqttitude = require('./mqttitude.js');
- and then put mqttitude.js in the same folder as server.js

Code: Select all

// go to http://mqttitude.org/ and install the android or ios app

// this is the location of the Eiffel Tower, but you can change it in whatever location you want (e.g. your home)
var home = new LatLon(48.858292,2.294723);

// thanks to Chris Veness for the calcuation stuff. http://www.movable-type.co.uk/scripts/latlong.html
//
// ---- extend Number object with methods for converting degrees/radians

/** Converts numeric degrees to radians */
if (typeof Number.prototype.toRad == 'undefined') {
  Number.prototype.toRad = function() {
    return this * Math.PI / 180;
  }
}

/** Converts radians to numeric (signed) degrees */
if (typeof Number.prototype.toDeg == 'undefined') {
  Number.prototype.toDeg = function() {
    return this * 180 / Math.PI;
  }
}
/** 
 * Formats the significant digits of a number, using only fixed-point notation (no exponential)
 * 
 * @param   {Number} precision: Number of significant digits to appear in the returned string
 * @returns {String} A string representation of number which contains precision significant digits
 */
if (typeof Number.prototype.toPrecisionFixed == 'undefined') {
  Number.prototype.toPrecisionFixed = function(precision) {
    
    // use standard toPrecision method
    var n = this.toPrecision(precision);
    
    // ... but replace +ve exponential format with trailing zeros
    n = n.replace(/(.+)e\+(.+)/, function(n, sig, exp) {
      sig = sig.replace(/\./, '');       // remove decimal from significand
      l = sig.length - 1;
      while (exp-- > l) sig = sig + '0'; // append zeros from exponent
      return sig;
    });
    
    // ... and replace -ve exponential format with leading zeros
    n = n.replace(/(.+)e-(.+)/, function(n, sig, exp) {
      sig = sig.replace(/\./, '');       // remove decimal from significand
      while (exp-- > 1) sig = '0' + sig; // prepend zeros from exponent
      return '0.' + sig;
    });
    
    return n;
  }
} 

/** Trims whitespace from string (q.v. blog.stevenlevithan.com/archives/faster-trim-javascript) */
if (typeof String.prototype.trim == 'undefined') {
  String.prototype.trim = function() {
    return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  }
}

function LatLon(lat, lon, rad) {
  if (typeof(rad) == 'undefined') rad = 6371;  // earth's mean radius in km
  // only accept numbers or valid numeric strings
  this._lat = typeof(lat)=='number' ? lat : typeof(lat)=='string' && lat.trim()!='' ? +lat : NaN;
  this._lon = typeof(lon)=='number' ? lon : typeof(lon)=='string' && lon.trim()!='' ? +lon : NaN;
  this._radius = typeof(rad)=='number' ? rad : typeof(rad)=='string' && trim(lon)!='' ? +rad : NaN;
}

LatLon.prototype.distanceTo = function(point, precision) {
  // default 4 sig figs reflects typical 0.3% accuracy of spherical model
  if (typeof precision == 'undefined') precision = 4;
  
  var R = this._radius;
  var lat1 = this._lat.toRad(), lon1 = this._lon.toRad();
  var lat2 = point._lat.toRad(), lon2 = point._lon.toRad();
  var dLat = lat2 - lat1;
  var dLon = lon2 - lon1;

  var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
          Math.cos(lat1) * Math.cos(lat2) * 
          Math.sin(dLon/2) * Math.sin(dLon/2);
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  var d = R * c;
  return d.toPrecisionFixed(precision);
}
/* here the mqttitude part starts. 
Put mqttitude = require
*/
mqtt = require('mqtt');


client = mqtt.createClient(1883, 'localhost');

client.subscribe('/mqttitude');
client.on('message', function (topic, message) {
  console.log('Mqttitude Received: '+ topic + ' ' + message);
  
  var lat = 0;
  var lon = 0;
  
  try {
    var payload = JSON.parse(message);  
    if (payload !=  null) {
		console.log('JSON Payload');
		if(payload.lat) {	
			lat = payload.lat;
		}
		if(payload.lon) {
			lon = payload.lon;
		}
	  }
		  
	var location = new LatLon(lat, lon);  
	var distance = home.distanceTo(location, 4);
	console.log('Lat = '+lat+' Lon = '+lon+' Distance = '+distance);
	
	var pubmessage = '{"SensorType":"counter","svalue":"'+distance+'km"}';
	console.log(pubmessage);
	client.publish('/actions/domoticz/mqttitude', pubmessage);
	
  }
  catch(e) {
    console.log("Could not parse payload");
    console.log(e);
  }
});
- start publishing your location from the android or iPhone app
- in Domoticz a rfxmeter device will be auto-discovered in the unused devices with the distance to the location in mqttitude.js (now the Eiffel Tower, but you can change this in your home location obviously).
sebas
Posts: 117
Joined: Tuesday 17 September 2013 7:46
Target OS: Linux
Domoticz version:
Location: Deventer, The Netherlands
Contact:

Re: MQTT Support

Post by sebas »

Started a Wiki page about MQTT support. We can update that while we're developing. It's here

Currently I'm using idx because that's easier but I would like to switch to the dtype's etc. for added flexibility. I like the idea to use SensorTypes that convert to dtype's etc. in the server.js. That makes it much easier for users. @maxtrash: for the sensors you've found already can you publish a list of the type's you're using? Than we can expand that with the one's we find. You're still working with node.js right?

I've created a public Github repository where we can host the source code. Can we work together on the server.js file there?
I'm currently on my commute home. I'll upload the source code for my server.js later this evening.

@andriej: in the Arduino sketch I've published you'll see a function at the bottom. This function is called everytime the Arduino receives a message from the Broker. Switching devices on/off can be implemented in there. You'll have to do some mapping between the devices in Domoticz and in the Arduino. The only value I know of that's unique would be the idx.

@maxtrash: In your source I read about a LUA script pushing updates. Is that a standard LUA script or something you wrote yourself?
maxtrash
Posts: 106
Joined: Tuesday 06 August 2013 1:31
Target OS: -
Domoticz version:
Contact:

Re: MQTT Support

Post by maxtrash »

Nice one, creating the wiki-page and github repository! I'll add stuff to later on. No experience yet with github. How should I upload code?

this is the LUA-script I made for Domoticz. I called it script_device_mqtt.lua and put it in domoticz\scripts\lua

Code: Select all

--[[
MQTT demo device script
 
devicechanges are pushed to localhost port 5001 if there's any value to publish
a node.js script is waiting and publishes using mqtt

NB. Domoticz will add _ and property to the name of certain sensors (e.g. _temperature, _humidity). This is passed as lowest level of message in mqtt
--]]

commandArray = {}

logging = true
d = otherdevices
device = ""
for i, v in pairs(devicechanged) do
  if (#device == 0 or #i < #device) then device = i end
  text = v..''
  if #text > 0 then
	text = string.gsub(i, "_", '/')
	text = '127.0.0.1:5001/'..text..'?'..v
	text = string.gsub(text, "%s+", '%%20')
	commandArray['OpenURL']=text
	if (logging) then print(text) end
  end
end

if (logging) then print('--- LUA Device script MQTT --- Triggered by ' .. device .. ' now ' .. d[device]) end

return commandArray
andriej
Posts: 46
Joined: Tuesday 10 December 2013 22:27
Target OS: Linux
Domoticz version: beta
Contact:

Re: MQTT Support

Post by andriej »

Ok, I've managed to put PubSub library onto my MEGA, wasn't that hard as I've optimized code for sensor/devices reporting via URL queries to Domoticz - earlier.

Code: Select all

$ mosquitto_sub -h localhost -t /# -v
/HAC Hello world, MQTT Arduino riporting furr djuti!
/HAC/IDX-test {"data": [{"idx": 58, "svalue": "1"}]}
/HAC/IDX-test {"data": [{"idx": 58, "svalue": "0"}]}
/HAC/IDX-test {"data": [{"idx": 58, "svalue": "0"}]}
/HAC/IDX-test {"data": [{"idx": 58, "svalue": "1"}]}
/HAC/IDX-test {"data": [{"idx": 58, "svalue": "0"}]}
/HAC/IDX-test {"data": [{"idx": 58, "svalue": "0"}]}
/HAC/IDX-test {"data": [{"idx": 58, "svalue": "0"}]}
/HAC/IDX-test {"data": [{"idx": 57, "svalue": "0"}]}
/HAC/IDX-test {"data": [{"idx": 58, "svalue": "0"}]}
Now I need to setup the rest, I guess.
Or before that I'll try to code the dunit/did part also.

And I also don't know how to use git as a tool. ;-)
Orange Pi (@ Debian) / MySensors Serial / GPIO / Custom Serial 433 MHz Transmitter (for plug switches and livolo wall switches) / JSON&Bash API scripts
maxtrash
Posts: 106
Joined: Tuesday 06 August 2013 1:31
Target OS: -
Domoticz version:
Contact:

Re: MQTT Support

Post by maxtrash »

if you want to use the latest domoticz.js you should try publishing to /actions/domoticz/hac
in the latest version I simplified the payload so it's not an array anymore.
Therefore you could try {"idx": 58, "svalue": "1"}

of course you can also make your own script in nodejs

@sebas: I think you started with the array in Json? I thought this is easier and if you want to send more than one json-action to domoticz, why not use two mqtt messages? What's your opinion?
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest