Browse Source

Added domoticz support to new sensor module

fastled
Xose Pérez 6 years ago
parent
commit
d426969fb2
6 changed files with 3300 additions and 3243 deletions
  1. BIN
      code/espurna/data/index.html.gz
  2. +9
    -14
      code/espurna/domoticz.ino
  3. +85
    -36
      code/espurna/sensor.ino
  4. +3049
    -3057
      code/espurna/static/index.html.gz.h
  5. +128
    -100
      code/html/custom.js
  6. +29
    -36
      code/html/index.html

BIN
code/espurna/data/index.html.gz View File


+ 9
- 14
code/espurna/domoticz.ino View File

@ -86,23 +86,18 @@ void _domoticzWebSocketOnSend(JsonObject& root) {
root["dczTopicIn"] = getSetting("dczTopicIn", DOMOTICZ_IN_TOPIC);
root["dczTopicOut"] = getSetting("dczTopicOut", DOMOTICZ_OUT_TOPIC);
JsonArray& dczRelayIdx = root.createNestedArray("dczRelayIdx");
JsonArray& relays = root.createNestedArray("dczRelayIdx");
for (byte i=0; i<relayCount(); i++) {
dczRelayIdx.add(domoticzIdx(i));
relays.add(domoticzIdx(i));
}
#if DHT_SUPPORT
root["dczTmpIdx"] = getSetting("dczTmpIdx").toInt();
root["dczHumIdx"] = getSetting("dczHumIdx").toInt();
#endif
#if DS18B20_SUPPORT
root["dczTmpIdx"] = getSetting("dczTmpIdx").toInt();
#endif
#if ANALOG_SUPPORT
root["dczAnaIdx"] = getSetting("dczAnaIdx").toInt();
#endif
JsonArray& sensors = root.createNestedArray("dczSensors");
for (byte i=0; i<magnitudeCount(); i++) {
JsonObject& sensor = sensors.createNestedObject();
sensor["name"] = magnitudeName(i);
sensor["type"] = magnitudeType(i);
sensor["idx"] = getSetting("dczSensor", i, 0).toInt();
}
#if POWER_PROVIDER != POWER_PROVIDER_NONE
root["dczPowIdx"] = getSetting("dczPowIdx").toInt();


+ 85
- 36
code/espurna/sensor.ino View File

@ -18,6 +18,8 @@ typedef struct {
unsigned char global; // Global index in its type
double current; // Current (last) value, unfiltered
double filtered; // Filtered (averaged) value
double reported; // Last reported value
double min_change; // Minimum value change to report
BaseFilter * filter; // Filter object
} sensor_magnitude_t;
@ -181,6 +183,25 @@ unsigned char sensorCount() {
return _sensors.size();
}
unsigned char magnitudeCount() {
return _magnitudes.size();
}
String magnitudeName(unsigned char index) {
if (index < _magnitudes.size()) {
sensor_magnitude_t magnitude = _magnitudes[index];
return magnitude.sensor->slot(magnitude.local);
}
return String();
}
unsigned char magnitudeType(unsigned char index) {
if (index < _magnitudes.size()) {
return int(_magnitudes[index].type);
}
return MAGNITUDE_NONE;
}
void sensorInterrupt(unsigned char sensor_id, unsigned char gpio, int mode) {
_sensor_isr = sensor_id;
attachInterrupt(gpio, sensorISR, mode);
@ -235,6 +256,8 @@ void sensorSetup() {
new_magnitude.global = _counts[type];
new_magnitude.current = 0;
new_magnitude.filtered = 0;
new_magnitude.reported = 0;
new_magnitude.min_change = 0;
if (type == MAGNITUDE_EVENTS) {
new_magnitude.filter = new MovingAverageFilter(SENSOR_REPORT_EVERY);
} else {
@ -277,7 +300,8 @@ void sensorLoop() {
last_update = millis();
report_count = (report_count + 1) % SENSOR_REPORT_EVERY;
double value;
double current;
double filtered;
char buffer[64];
// Pre-read hook
@ -292,19 +316,19 @@ void sensorLoop() {
unsigned char decimals = _sensorDecimals(magnitude.type);
value = magnitude.sensor->value(magnitude.local);
magnitude.filter->add(value);
current = magnitude.sensor->value(magnitude.local);
magnitude.filter->add(current);
// Special case
if (magnitude.type == MAGNITUDE_EVENTS) value = magnitude.filter->result();
if (magnitude.type == MAGNITUDE_EVENTS) current = magnitude.filter->result();
value = _sensorProcess(magnitude.type, value);
_magnitudes[i].current = value;
current = _sensorProcess(magnitude.type, current);
_magnitudes[i].current = current;
// Debug
#if TRUE
#if true
{
dtostrf(value, 1-sizeof(buffer), decimals, buffer);
dtostrf(current, 1-sizeof(buffer), decimals, buffer);
DEBUG_MSG("[SENSOR] %s - %s: %s%s\n",
magnitude.sensor->name().c_str(),
_sensorTopic(magnitude.type).c_str(),
@ -314,39 +338,64 @@ void sensorLoop() {
}
#endif
// Time to report (we do it every SENSOR_REPORT_EVERY readings)
if (report_count == 0) {
// TODO: option to report only if it has change (configurable amount)
value = magnitude.filter->result();
value = _sensorProcess(magnitude.type, value);
_magnitudes[i].filtered = value;
filtered = magnitude.filter->result();
magnitude.filter->reset();
dtostrf(value, 1-sizeof(buffer), decimals, buffer);
#if MQTT_SUPPORT
if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
mqttSend(_sensorTopic(magnitude.type).c_str(), magnitude.global, buffer);
} else {
mqttSend(_sensorTopic(magnitude.type).c_str(), buffer);
}
#endif
#if INFLUXDB_SUPPORT
if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
idbSend(_sensorTopic(magnitude.type).c_str(), magnitude.global, buffer);
} else {
idbSend(_sensorTopic(magnitude.type).c_str(), buffer);
filtered = _sensorProcess(magnitude.type, filtered);
_magnitudes[i].filtered = filtered;
// Check if there is a minimum change threshold to report
if (fabs(filtered - magnitude.reported) >= magnitude.min_change) {
_magnitudes[i].reported = filtered;
dtostrf(filtered, 1-sizeof(buffer), decimals, buffer);
#if MQTT_SUPPORT
if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
mqttSend(_sensorTopic(magnitude.type).c_str(), magnitude.global, buffer);
} else {
mqttSend(_sensorTopic(magnitude.type).c_str(), buffer);
}
#endif
#if INFLUXDB_SUPPORT
if (SENSOR_USE_INDEX || (_counts[magnitude.type] > 1)) {
idbSend(_sensorTopic(magnitude.type).c_str(), magnitude.global, buffer);
} else {
idbSend(_sensorTopic(magnitude.type).c_str(), buffer);
}
#endif
#if DOMOTICZ_SUPPORT
{
char key[15];
snprintf_P(key, sizeof(key), PSTR("dczSensor%d"), i);
if (magnitude.type == MAGNITUDE_HUMIDITY) {
int status;
if (filtered > 70) {
status = HUMIDITY_WET;
} else if (filtered > 45) {
status = HUMIDITY_COMFORTABLE;
} else if (filtered > 30) {
status = HUMIDITY_NORMAL;
} else {
status = HUMIDITY_DRY;
}
char status_buf[5];
itoa(status, status_buf, 10);
domoticzSend(key, buffer, status_buf);
} else {
domoticzSend(key, 0, buffer);
}
}
#endif
#endif
#if DOMOTICZ_SUPPORT
// TODO
#endif
}
}
}
} // if (fabs(filtered - magnitude.reported) >= magnitude.min_change)
} // if (report_count == 0)
} // if (magnitude.sensor->status())
} // for (unsigned char i=0; i<_magnitudes.size(); i++)
// Post-read hook
_sensorPost();


+ 3049
- 3057
code/espurna/static/index.html.gz.h
File diff suppressed because it is too large
View File


+ 128
- 100
code/html/custom.js View File

@ -336,60 +336,43 @@ function toggleMenu() {
}
// -----------------------------------------------------------------------------
// Templates
// Domoticz
// -----------------------------------------------------------------------------
function createRelays(count) {
function createRelayIdxs(data) {
var current = $("#relays > div").length;
var current = $("#domoticzRelays > div").length;
if (current > 0) return;
var template = $("#relayTemplate .pure-g")[0];
for (var relayID=0; relayID<count; relayID++) {
var template = $("#relayIdxTemplate .pure-g")[0];
for (var i=0; i<data.length; i++) {
var line = $(template).clone();
$(line).find("input").each(function() {
$(this).attr("data", relayID);
});
if (count > 1) $(".relay_id", line).html(" " + (relayID+1));
line.appendTo("#relays");
$(":checkbox", line).iphoneStyle({
onChange: doToggle,
resizeContainer: true,
resizeHandle: true,
checkedLabel: 'ON',
uncheckedLabel: 'OFF'
});
if (data.length > 1) $("label", line).html("Switch #" + i);
$("input", line).attr("name", "dczRelayIdx" + i).attr("tabindex", 40 + i).val(data[i]);
line.appendTo("#domoticzRelays");
}
}
function createIdxs(count) {
function createSensorIdxs(data) {
var current = $("#idxs > div").length;
var current = $("#domoticzSensors > div").length;
if (current > 0) return;
var template = $("#idxTemplate .pure-g")[0];
for (var id=0; id<count; id++) {
var template = $("#sensorIdxTemplate .pure-g")[0];
for (var i=0; i<data.length; i++) {
var line = $(template).clone();
$(line).find("input").each(function() {
$(this).attr("data", id).attr("tabindex", 40+id);
});
if (count > 1) $(".id", line).html(" " + id);
line.appendTo("#idxs");
$("label", line).html(sensorType(data[i].type));
$("div.hint", line).html(data[i].name);
$("input", line).attr("name", "dczSensor" + i).attr("tabindex", 40 + i).val(data[i].idx);
line.appendTo("#domoticzSensors");
}
}
function delNetwork() {
var parent = $(this).parents(".pure-g");
$(parent).remove();
}
function moreNetwork() {
var parent = $(this).parents(".pure-g");
$("div.more", parent).toggle();
}
// -----------------------------------------------------------------------------
// Wifi
// -----------------------------------------------------------------------------
function addNetwork() {
@ -413,6 +396,43 @@ function addNetwork() {
}
function delNetwork() {
var parent = $(this).parents(".pure-g");
$(parent).remove();
}
function moreNetwork() {
var parent = $(this).parents(".pure-g");
$("div.more", parent).toggle();
}
// -----------------------------------------------------------------------------
// Relays
// -----------------------------------------------------------------------------
function initRelays(data) {
var current = $("#relays > div").length;
if (current > 0) return;
var template = $("#relayTemplate .pure-g")[0];
for (var i=0; i<data.length; i++) {
var line = $(template).clone();
if (data.length > 1) $(".relay_id", line).html(" " + (i+1));
$("input", line).attr("data", i).prop("checked", data[i]);
line.appendTo("#relays");
$(":checkbox", line).iphoneStyle({
onChange: doToggle,
resizeContainer: true,
resizeHandle: true,
checkedLabel: 'ON',
uncheckedLabel: 'OFF'
}).iphoneStyle("refresh");
}
}
function addRelayGroup() {
var numGroups = $("#relayGroups > div").length;
@ -430,6 +450,10 @@ function addRelayGroup() {
}
// -----------------------------------------------------------------------------
// Sensors
// -----------------------------------------------------------------------------
function initSensors(data) {
// check if already initialized
@ -448,6 +472,10 @@ function initSensors(data) {
}
// -----------------------------------------------------------------------------
// Lights
// -----------------------------------------------------------------------------
function initColorRGB() {
// check if already initialized
@ -538,6 +566,10 @@ function initChannels(num) {
}
// -----------------------------------------------------------------------------
// RFBridge
// -----------------------------------------------------------------------------
function addRfbNode() {
var numNodes = $("#rfbNodes > fieldset").length;
@ -583,8 +615,6 @@ function rfbSend() {
function processData(data) {
console.log(data);
// title
if ("app_name" in data) {
var title = data.app_name;
@ -600,32 +630,31 @@ function processData(data) {
Object.keys(data).forEach(function(key) {
// Web Modes
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Web mode
// ---------------------------------------------------------------------
if (key == "webMode") {
password = data.webMode == 1;
$("#layout").toggle(data.webMode == 0);
$("#password").toggle(data.webMode == 1);
}
// ---------------------------------------------------------------------
// Actions
if (key == "action") {
if (data.action == "reload") {
doReload(1000);
}
if (data.action == "rfbLearn") {
// Nothing to do?
}
if (data.action == "rfbTimeout") {
// Nothing to do?
}
// ---------------------------------------------------------------------
if (key == "action") {
if (data.action == "reload") doReload(1000);
return;
}
// ---------------------------------------------------------------------
// RFBridge
// ---------------------------------------------------------------------
if (key == "rfbCount") {
for (var i=0; i<data.rfbCount; i++) addRfbNode();
return;
@ -641,6 +670,10 @@ function processData(data) {
return;
}
// ---------------------------------------------------------------------
// Lights
// ---------------------------------------------------------------------
if (key == "rgb") {
initColorRGB();
$("input[name='color']").wheelColorPicker('setValue', data[key], true);
@ -679,35 +712,32 @@ function processData(data) {
return;
}
if (key == "uptime") {
var uptime = parseInt(data[key]);
var seconds = uptime % 60; uptime = parseInt(uptime / 60);
var minutes = uptime % 60; uptime = parseInt(uptime / 60);
var hours = uptime % 24; uptime = parseInt(uptime / 24);
var days = uptime;
data[key] = days + 'd ' + zeroPad(hours, 2) + 'h ' + zeroPad(minutes, 2) + 'm ' + zeroPad(seconds, 2) + 's';
}
if (key == "useWhite") {
useWhite = data[key];
}
if (key == "maxNetworks") {
maxNetworks = parseInt(data.maxNetworks);
return;
}
// ---------------------------------------------------------------------
// Sensors
// ---------------------------------------------------------------------
if (key == "sensors") {
initSensors(data[key]);
for (var i=0; i<data[key].length; i++) {
var element = $("input[data=" + i + "]");
var element = $("input[name=sensor][data=" + i + "]");
if (element.length) element.val(data[key][i].value + data[key][i].units);
}
return;
}
// Wifi
// ---------------------------------------------------------------------
// WiFi
// ---------------------------------------------------------------------
if (key == "maxNetworks") {
maxNetworks = parseInt(data.maxNetworks);
return;
}
if (key == "wifi") {
var networks = data.wifi;
@ -730,23 +760,13 @@ function processData(data) {
}
// Relay status
if (key == "relayStatus") {
var relays = data.relayStatus;
createRelays(relays.length);
for (var relayID in relays) {
var element = $(".relayStatus[data=" + relayID + "]");
if (element.length > 0) {
element
.prop("checked", relays[relayID])
.iphoneStyle("refresh");
}
}
// ---------------------------------------------------------------------
// Relays
// ---------------------------------------------------------------------
if (key == "relayStatus") {
initRelays(data[key]);
return;
}
// Relay groups
@ -771,19 +791,25 @@ function processData(data) {
return;
}
// ---------------------------------------------------------------------
// Domoticz
if (key == "dczRelayIdx") {
var idxs = data.dczRelayIdx;
createIdxs(idxs.length);
// ---------------------------------------------------------------------
for (var i in idxs) {
var element = $(".dczRelayIdx[data=" + i + "]");
if (element.length > 0) element.val(idxs[i]);
}
// Domoticz - Relays
if (key == "dczRelayIdx") {
createRelayIdxs(data[key]);
return;
}
// Domoticz - Sensors
if (key == "dczSensors") {
createSensorIdxs(data[key]);
return;
}
// ---------------------------------------------------------------------
// General
// ---------------------------------------------------------------------
// Messages
if (key == "message") {
@ -808,17 +834,19 @@ function processData(data) {
if (key == "ntpStatus") {
data.ntpStatus = data.ntpStatus ? "SYNC'D" : "NOT SYNC'D";
}
if (key == "tmpUnits") {
$("span[name='tmpUnits']").html(data[key] == 1 ? "ºF" : "ºC");
}
if (key == "dhtConnected" && !data[key]) {
$("input[name='dhtTmp']").val("NOT CONNECTED");
$("input[name='dhtHum']").val("NOT CONNECTED");
}
if (key == "dsConnected" && !data[key]) {
$("input[name='dsTmp']").val("NOT CONNECTED");
if (key == "uptime") {
var uptime = parseInt(data[key]);
var seconds = uptime % 60; uptime = parseInt(uptime / 60);
var minutes = uptime % 60; uptime = parseInt(uptime / 60);
var hours = uptime % 24; uptime = parseInt(uptime / 24);
var days = uptime;
data[key] = days + 'd ' + zeroPad(hours, 2) + 'h ' + zeroPad(minutes, 2) + 'm ' + zeroPad(seconds, 2) + 's';
}
// ---------------------------------------------------------------------
// Matching
// ---------------------------------------------------------------------
// Look for INPUTs
var element = $("input[name=" + key + "]");
if (element.length > 0) {


+ 29
- 36
code/html/index.html View File

@ -291,7 +291,8 @@
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4">Double click delay</label>
<input name="btnDelay" class="pure-u-1 pure-u-md-3-4" type="number" action="reboot" min="0" step="100" max="1000" tabindex="6" />
<input name="btnDelay" class="pure-u-1 pure-u-md-1-4" type="number" action="reboot" min="0" step="100" max="1000" tabindex="6" />
<div class="pure-u-0 pure-u-md-1-2">&nbsp;</div>
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">
Delay in milliseconds to detect a double click (from 0 to 1000ms).<br />
@ -304,14 +305,13 @@
<div class="pure-g module module-led">
<label class="pure-u-1 pure-u-md-1-4">LED mode</label>
<div class="pure-u-1 pure-u-md-3-4">
<select name="ledMode0" tabindex="7">
<option value="1">WiFi status</option>
<option value="0">MQTT managed</option>
<option value="4">Find me</option>
<option value="5">Mixed</option>
</select>
</div>
<select name="ledMode0" class="pure-u-1 pure-u-md-1-4" tabindex="7">
<option value="1">WiFi status</option>
<option value="0">MQTT managed</option>
<option value="4">Find me</option>
<option value="5">Mixed</option>
</select>
<div class="pure-u-0 pure-u-md-1-2">&nbsp;</div>
<div class="pure-u-0 pure-u-md-1-4">&nbsp;</div>
<div class="pure-u-1 pure-u-md-3-4 hint">
This setting defines the behaviour of the main LED in the board.<br />
@ -345,8 +345,10 @@
<div class="pure-g module module-temperature">
<label class="pure-u-1 pure-u-sm-1-4">Temperature units</label>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="16" value="0"> Celsius (&deg;C)</input></div>
<div class="pure-u-1 pure-u-sm-1-4"><input type="radio" name="tmpUnits" tabindex="17" value="1"> Fahrenheit (&deg;F)</input></div>
<select name="tmpUnits" tabindex="16" class="pure-u-1 pure-u-md-1-4">
<option value="0">Celsius (&deg;C)</option>
<option value="1">Fahrenheit (&deg;F)</option>
</select>
</div>
<div class="pure-g module module-temperature">
@ -641,22 +643,22 @@
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4">MQTT Broker</label>
<input class="pure-u-1 pure-u-md-3-4" name="mqttServer" type="text" size="20" tabindex="21" placeholder="IP or address of your broker" />
<input class="pure-u-1 pure-u-md-1-4" name="mqttServer" type="text" size="20" tabindex="21" placeholder="IP or address of your broker" />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4">MQTT Port</label>
<input class="pure-u-1 pure-u-md-3-4" name="mqttPort" type="text" size="6" tabindex="22" value="1883" />
<input class="pure-u-1 pure-u-md-1-4" name="mqttPort" type="text" size="6" tabindex="22" value="1883" />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4">MQTT User</label>
<input class="pure-u-1 pure-u-md-3-4" name="mqttUser" type="text" size="20" tabindex="23" placeholder="Leave blank if no user/pass" autocomplete="false" />
<input class="pure-u-1 pure-u-md-1-4" name="mqttUser" type="text" size="20" tabindex="23" placeholder="Leave blank if no user" autocomplete="false" />
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-md-1-4">MQTT Password</label>
<input class="pure-u-1 pure-u-md-3-4" name="mqttPassword" type="password" size="20" tabindex="24" placeholder="Leave blank if no user/pass" autocomplete="false" />
<input class="pure-u-1 pure-u-md-1-4" name="mqttPassword" type="password" size="20" tabindex="24" placeholder="Leave blank if no pass" autocomplete="false" />
</div>
<div class="pure-g module module-mqttssl">
@ -785,17 +787,9 @@
<input class="pure-u-1 pure-u-md-3-4" name="dczTopicOut" type="text" action="reconnect" tabindex="32" />
</div>
<div class="pure-g module module-dht module-ds">
<label class="pure-u-1 pure-u-sm-1-4">Temperature IDX</label>
<div class="pure-u-1 pure-u-sm-1-8"><input class="pure-u-sm-23-24" name="dczTmpIdx" type="number" min="0" tabindex="33" data="0" /></div>
<div class="pure-u-1 pure-u-sm-5-8 hint center">Set to 0 to disable notifications.</div>
</div>
<div id="domoticzRelays"></div>
<div class="pure-g module module-dht">
<label class="pure-u-1 pure-u-sm-1-4">Humidity IDX</label>
<div class="pure-u-1 pure-u-sm-1-8"><input class="pure-u-sm-23-24" name="dczHumIdx" type="number" min="0" tabindex="34" data="0" /></div>
<div class="pure-u-1 pure-u-sm-5-8 hint center">Set to 0 to disable notifications.</div>
</div>
<div id="domoticzSensors"></div>
<div class="pure-g module module-pwr">
<label class="pure-u-1 pure-u-sm-1-4">Power IDX</label>
@ -821,15 +815,6 @@
<div class="pure-u-1 pure-u-sm-5-8 hint center">Set to 0 to disable notifications.</div>
</div>
<div class="pure-g module module-analog">
<label class="pure-u-1 pure-u-sm-1-4">Analog IDX</label>
<div class="pure-u-1 pure-u-sm-1-8"><input class="pure-u-sm-23-24" name="dczAnaIdx" type="number" min="0" tabindex="39" data="0" /></div>
<div class="pure-u-1 pure-u-sm-5-8 hint center">Set to 0 to disable notifications.</div>
</div>
<div id="idxs">
</div>
</fieldset>
</div>
@ -1088,14 +1073,22 @@
</div>
</div>
<div id="idxTemplate" class="template">
<div id="relayIdxTemplate" class="template">
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4">Switch<span class="id"></span> IDX</label>
<label class="pure-u-1 pure-u-sm-1-4">Switch</label>
<div class="pure-u-1 pure-u-sm-1-8"><input class="pure-u-sm-23-24 dczRelayIdx" name="dczRelayIdx" type="number" min="0" tabindex="0" data="0" /></div>
<div class="pure-u-1 pure-u-sm-5-8 hint center">Set to 0 to disable notifications.</div>
</div>
</div>
<div id="sensorIdxTemplate" class="template">
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4">Sensor</label>
<div class="pure-u-1 pure-u-sm-1-8"><input class="pure-u-sm-23-24" name="dczSensor" type="number" min="0" tabindex="0" data="0" /></div>
<div class="pure-u-1 pure-u-sm-5-8 hint center"></div>
</div>
</div>
<div id="colorRGBTemplate" class="template">
<div class="pure-g">
<label class="pure-u-1 pure-u-sm-1-4">Color</label>


Loading…
Cancel
Save