Hello.
I hope someone can help me with an issue I'm facing while using mySensors binding for rolling shutters and using V_VAR channels.
My Setup:
Software:
openHAB 2.5.5 stable release
MySensors openHAB binding 2.5.0.202002161928
Hardware:
MySensors Ethernet gateway
Rollershutters with functions UP/DOWN and TILT UP/TILT DOWN (7 TILT positions, like horizontal blinds) (no KNX!)
The plan:
Whenever openHAB starts I want openHAB to request the status of my rollershutter positions (UP or DOWN) and blind positions (1/7, 2/7 ... 7/7).
Whenever the rollershutter position or blind position changes the node is supposed to send the new status to the controller/openHAB.
Implementation:
On the mySensors node I keep variables for rollershutter positions (0% = open to 100% = closed) and tilt-position (1/7 = min tilt to 7/7 fully tilt).
The node sends both positions either when a command has been executed to change them (UP/DOWN or TILT-UP/TILT-DOWN) or when the controller requests them (via request command = message type = C_REQ).
I've implemented an openHAB rule which priodically (for testing purposes every minute) requests the positions of the rollershutters and blind-position from the node, the node responds with both positions using V_VAR3 (rollershutter position) and V_VAR4 (tilt position).
These requests cprrectly arrive at the node and the node responds with its position-values.
Problem:
When replying with the rollershutter position to the openhab rule request with e.g. 100 I get error "value must be between 0 and 100.
When replying with the tilt position to the openhab rule request with e.g. 1 (for 1/7) or 0.14 (for 1/7th) the openHAB log shows error "value must be between 0 and 100.
Whenever the node sends values 0 or 1 openhab accepts the values!
What I tried so far:
I assumed openHAB expects a percent value hence requiring a value between 0 and 1 (even though it says it must be between 0 and 100) so I tried to adjust my mySensors code to send decimals bt. 0 and 1 to represent values bt. 0 and 100 (percent) - no luck though!
The log only shows 1.0 or 0.0 being received from node eventhough the node sends float(shutter position) / 100.0 for rollershutter position or float(tilt position) / 7.0 for tilt position.
I've tried different formatting in the items file ([%.2f], [%d], none and many others) - no help!
Is it possible that there is a bug in the binding implementation of V_VAR1 to V_VAR4 ?
It seems to only accept values 0 and 1 for the V_VAR channels.
Is it me? Am I missing anything here?
Any help is highly appreciated!
Thanks,
Ralph...
Things:
Bridge mysensors:bridge-eth:gateway @ "Wohnzimmer" [ ipAddress="10.0.0.11",
tcpPort=5003,
startupCheckEnabled=true,
imperial=false,
sendDelay=200,
networkSanCheckSendHeartbeat=false,
networkSanCheckInterval=3,
networkSanCheckSendHeartbeatFailAttempts=10,
networkSanCheckEnabled=true,
networkSanCheckConnectionFailAttempts=3 ]
{
cover BueroRalphJalousie "Jalousie" @ "Büro Ralph" [ nodeId=7, childId=4, requestAck=false ]
}
Items:
Rollershutter BueroRalphJalousieAufAb "Auf/ab" { channel="mysensors:cover:gateway:BueroRalphJalousie:cover" }
Number BueroRalphJalousieStatusAufAb "Status Auf/ab [%d]" { channel="mysensors:cover:gateway:BueroRalphJalousie:var3" }
Number BueroRalphJalousieStatusTilt "Status Tilt [%.2f]" { channel="mysensors:cover:gateway:BueroRalphJalousie:var4" }
Rule:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Stati von Node erfragen:
rule HoleUnbekannteStati
when
Time cron "0 0/1 * 1/1 * ? *" //alle 1 Minuten
then
// Jalousiesteuerung - UG:
if ( BueroRalphJalousieStatusAufAb.state == NULL
|| BueroRalphJalousieStatusTilt.state == NULL )
{
// make the node send in positions for rollershutters and blinds (yes it sends both even though only one (V_VAR3) is being requested!):
sendCommand(mySMsg01, "7;4;2;0;26"); //erfrage Jalousie-AufAb-Status Buero Ralph von Node 7, Child 4, 2=request, 0 = kein acknowlege, 26 = V_VAR3
if ( DebugLogger.state == ON )
{
logInfo( "Jalousien.Rules.HoleUnbekannteStati", "Jalousie-Stati Buero Ralph werden von Node erfragt")
}
}
end
mySensors code (excerpt):
void receive(const MyMessage &IncomingMessage)
{
// Eingehende Anfrage:
if (IncomingMessage.destination == MY_NODE_ID
&& IncomingMessage.getCommand() == C_REQ ) //Wert fuer ein Child wird abgefragt!
{
for (byte i = 1; i <= AnzahlRaeume; i++) {
if (i == IncomingMessage.sensor)
{
if (IncomingMessage.type == V_VAR3
|| IncomingMessage.type == V_VAR4)
{
send(Jalousien[i - 1].MessageStatusAufAb.set(Jalousien[i - 1].StatusAufAb, 1), false);
send(Jalousien[i - 1].MessageStatusTilt.set(Jalousien[i - 1].StatusTilt, 1), false);
}
}
}
}
}
mySensors code (full copy):
#include <Arduino.h>
//#define MY_DEBUG
#define MY_RADIO_RF24
#define MY_NODE_ID 7
#define MY_REPEATER_FEATURE
#include <MySensors.h>
#define MY_RF24_PA_LEVEL RF24_PA_HIGH
//#define ACTIVE_LOW // comment out this line if your relays are active high
#ifdef ACTIVE_LOW
#define BitShiftRelaisNummer ~(1U << (RelaisNummer-1))
#define AlleRelaisAus 0xFFFF
#else
#define BitShiftRelaisNummer (1U << (RelaisNummer-1))
#define AlleRelaisAus 0U
#endif
int RelaisNummer;
#define AnzahlRaeume 8 // Anzahl der Raeume
//Setup Shift Register:
const int clockPin = 4;
const int outputEnablePin = 6;
const int dataPin = 7;
const int latchPin = 8;
typedef enum
{
V_TILT_UP = 90, V_TILT_DOWN = 91, V_TILT_UP3 = 92, V_TILT_DOWN3 = 93,
} custom_mysensors_data_t;
typedef struct Jalousie
{
uint8_t RaumNummer;
char *RaumName;
int RelaisNummerAuf;
int RelaisNummerAb;
int Fahrzeit;
int TiltSchritte;
int Command;
uint8_t StatusAufAb; // (0=UP, 100=DOWN, 99=unknown), StatusTilt (0=UP)
uint8_t StatusTilt; // (0=OPEN, 7=CLOSE, 9=unknown), StatusTilt (0=UP)
MyMessage MessageStatusAufAb;
MyMessage MessageStatusTilt;
} JalousieTyp;
JalousieTyp Jalousien[AnzahlRaeume] = //RaumNummer, RaumName, RalaisNummer Funktion "AUF", RelaisNummer Funktion "AB", Fahrzeit in Sek., TiltSchritte, Command, StatusAufAb (0=UP, 100=DOWN, 99=unknown), StatusTilt (0=UP, 100=DOWN, 99=unknown)
{
{ 1, "Büro Christine", 1, 2, 45, 7, 0, 99, 0 },
{ 2, "Spielzimmer", 3, 4, 65, 7, 0, 99, 0 },
{ 3, "Nebenküche", 5, 6, 45, 7, 0, 99, 0 },
{ 4, "Büro Ralph", 7, 8, 45, 7, 0, 99, 0 },
{ 5, "Esszimmer", 9, 10, 65, 7, 0, 99, 0 },
{ 6, "Wohnzimmer Terrasse", 11, 12, 65, 7, 0, 99, 0 },
{ 7, "Wohnzimmer Garten", 13, 14, 65, 7, 0, 99, 0 },
{ 8, "Küche", 15, 16, 65, 7, 0, 99, 0 }
};
////////////////////////////////////////////////////////////////////////////////
void setup()
{
#ifdef DEBUG_ON
Serial.println();
Serial.println("SETUP - BEGIN");
#endif
// BitShiftRegister vorbereiten:
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(outputEnablePin, OUTPUT);
digitalWrite(outputEnablePin, LOW);
// BitShiftRegister auf "alles aus" schalten:
digitalWrite(latchPin, LOW); //DatenPipeline fuer BitShiftRegister "lahm" legen
shiftOut(dataPin, clockPin, MSBFIRST, AlleRelaisAus); //DatenPipeline in das BitShiftRegister laden:
shiftOut(dataPin, clockPin, MSBFIRST, AlleRelaisAus);
digitalWrite(latchPin, HIGH); //neue Daten im BitShiftRegister aktivieren:
// Jalousie-Messages vorbereiten:
#ifdef DEBUG_ON
Serial.println();
Serial.println("RaumNummer, RelaisName, RelaisNr-AUF, RelaisNr-AB, Fahrzeit, Command, StatusAufAb, StatusTilt");
#endif
for (byte i = 1; i <= AnzahlRaeume; i++) {
Jalousien[i - 1].MessageStatusAufAb.setSensor(Jalousien[i - 1].RaumNummer); //ChildID
Jalousien[i - 1].MessageStatusAufAb.setType(V_VAR3);
Jalousien[i - 1].MessageStatusTilt.setSensor(Jalousien[i - 1].RaumNummer); //ChildID
Jalousien[i - 1].MessageStatusTilt.setType(V_VAR4);
#ifdef DEBUG_ON
Serial.println(Jalousien[i - 1].RaumNummer + ", " + Jalousien[i - 1].RaumName, + ", " + Jalousien[i - 1].RelaisNummerAuf + ", " + Jalousien[i - 1].RelaisNummerAb + ", " + Jalousien[i - 1].Fahrzeit + ", " + Jalousien[i - 1].Command + ", " + Jalousien[i - 1].StatusAuf + ", " + Jalousien[i - 1].StatusAb);
#endif
}
#ifdef MY_DEBUG
Serial.println();
Serial.println("SETUP - END");
#endif
}
//////////////////////////////////////////////////////////////////////////////////////
void presentation()
{
#ifdef MY_DEBUG
Serial.println();
Serial.println("PRESENTATION - BEGIN");
#endif
sendSketchInfo("Jalousie-Controller EG", "1.1");
for (byte i = 1; i <= AnzahlRaeume; i++) {
present(Jalousien[i - 1].RaumNummer, S_COVER, Jalousien[i - 1].RaumName, false); //Jalousie
}
#ifdef MY_DEBUG
Serial.println();
Serial.println("PRESENTATION - END");
#endif
}
//////////////////////////////////////////////////////////////////////////////
void JalousieBewegen(int RaumNummer, uint8_t Command)
{
const int DauerTilt = 150;
const int DauerFahren = 1100;
int Signaldauer = DauerTilt;
int cycles = 1;
#ifdef MY_DEBUG
Serial.println();
Serial.println("JalousieFahren - BEGIN");
Serial.print("Command: ");
Serial.println(Command);
#endif
// RelaisNummer (Funktion also AUF- oder AB-Relais) ermitteln und Signaldauer (Fahren oder TILT) festlegen:
switch (Command) {
case V_UP:
RelaisNummer = Jalousien[RaumNummer - 1].RelaisNummerAuf;
Signaldauer = DauerFahren;
Jalousien[RaumNummer - 1].StatusAufAb = 0;
Jalousien[RaumNummer - 1].StatusTilt = 0;
break;
case V_DOWN:
RelaisNummer = Jalousien[RaumNummer - 1].RelaisNummerAb;
Signaldauer = DauerFahren;
Jalousien[RaumNummer - 1].StatusAufAb = 100;
Jalousien[RaumNummer - 1].StatusTilt = Jalousien[RaumNummer - 1].TiltSchritte;
break;
case V_STOP:
RelaisNummer = Jalousien[RaumNummer - 1].RelaisNummerAuf;
Jalousien[RaumNummer - 1].StatusAufAb = 99;
break;
case V_PERCENTAGE:
// Hole Prozentsatz (0-100 oder STOP):
// to be implemented
RelaisNummer = 0;
break;
case V_TILT_UP: //90 TILT-UP
RelaisNummer = Jalousien[RaumNummer - 1].RelaisNummerAuf;
if (Jalousien[RaumNummer - 1].StatusTilt >= cycles)
{
Jalousien[RaumNummer - 1].StatusTilt = Jalousien[RaumNummer - 1].StatusTilt - cycles;
}
break;
case V_TILT_DOWN: //"91 TILT-DOWN"
RelaisNummer = Jalousien[RaumNummer - 1].RelaisNummerAb;
if (Jalousien[RaumNummer - 1].StatusTilt <= ( Jalousien[RaumNummer - 1].TiltSchritte - cycles ))
{
Jalousien[RaumNummer - 1].StatusTilt = Jalousien[RaumNummer - 1].StatusTilt + 1;
}
break;
case V_TILT_UP3: //"92 TILT-UP3"
RelaisNummer = Jalousien[RaumNummer - 1].RelaisNummerAuf;
cycles = 3;
if (Jalousien[RaumNummer - 1].StatusTilt >= cycles)
{
Jalousien[RaumNummer - 1].StatusTilt = Jalousien[RaumNummer - 1].StatusTilt - cycles;
}
break;
case V_TILT_DOWN3: //"93 TILT-DOWN3"
RelaisNummer = Jalousien[RaumNummer - 1].RelaisNummerAb;
cycles = 3;
if (Jalousien[RaumNummer - 1].StatusTilt <= (Jalousien[RaumNummer - 1].TiltSchritte - cycles))
{
Jalousien[RaumNummer - 1].StatusTilt = Jalousien[RaumNummer - 1].StatusTilt + cycles;
}
break;
};
#ifdef MY_DEBUG
Serial.print("Raumnummer: ");
Serial.println(RaumNummer);
Serial.print("JalousieRelaisNummer: ");
Serial.println(RelaisNummer);
Serial.print("BitShift-Relais-Nummer: ");
Serial.println(BitShiftRelaisNummer);
#endif
// Relais (FunktionsNummer) fuer Signaldauer schalten:
if (RelaisNummer != 0)
{
for (int i = 1; i <= cycles; i++)
{
// Relais einschalten:
#ifdef MY_DEBUG
Serial.println("Schaltet ein");
#endif
digitalWrite(latchPin, LOW); //DatenPipeline fuer BitShiftRegister "lahm" legen
shiftOut(dataPin, clockPin, MSBFIRST, highByte(BitShiftRelaisNummer)); //DatenPipeline in das BitShiftRegister laden
shiftOut(dataPin, clockPin, MSBFIRST, lowByte(BitShiftRelaisNummer));
digitalWrite(latchPin, HIGH); //neue Daten im BitShiftRegister aktivieren
// Relais "gedrueckt" lassen:
#ifdef MY_DEBUG
Serial.print("wartet ");
Serial.print(Signaldauer);
Serial.println(" ms");
#endif
wait(Signaldauer);
// Relais ausschalten:
#ifdef MY_DEBUG
Serial.println("Schaltet aus");
#endif
digitalWrite(latchPin, LOW); //DatenPipeline fuer BitShiftRegister "lahm" legen
shiftOut(dataPin, clockPin, MSBFIRST, AlleRelaisAus); //DatenPipeline in das BitShiftRegister laden
shiftOut(dataPin, clockPin, MSBFIRST, AlleRelaisAus); //doppelt???
digitalWrite(latchPin, HIGH); //neue Daten im BitShiftRegister aktivieren
if (cycles > 1) {
wait(750);
}
}
}
// Neuen Zustand an Controller melden:
send(Jalousien[RaumNummer - 1].MessageStatusAufAb.set(Jalousien[RaumNummer - 1].StatusAufAb, 1), false);
send(Jalousien[RaumNummer - 1].MessageStatusTilt.set(Jalousien[RaumNummer - 1].StatusTilt, 1), false);
#ifdef MY_DEBUG
Serial.println("JalousieFahren - END");
#endif
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
#ifdef MY_DEBUG
Serial.println();
Serial.println("LOOP - BEGIN");
#endif
for (byte i = 1; i <= AnzahlRaeume; i++)
{
#ifdef MY_DEBUG
Serial.print('RaumNummer / Kommando: ');
Serial.print(' ');
Serial.print(i);
Serial.print(' / ');
Serial.println(Jalousien[i-1].Command);
#endif
if (Jalousien[i - 1].Command != 0)
{
JalousieBewegen(Jalousien[i - 1].RaumNummer, Jalousien[i - 1].Command); //RaumNummer, Kommando
Jalousien[i - 1].Command = 0;
}
}
#ifdef MY_DEBUG
Serial.println();
Serial.println("LOOP - END");
wait(4000);
#endif
}
////////////////////////////////////////////////////////////////////////////////
void receive(const MyMessage &IncomingMessage)
{
String Payload;
Payload = IncomingMessage.getString();
#ifdef MY_DEBUG
Serial.println();
Serial.println("RECEIVE - BEGIN");
Serial.println();
Serial.println("Nachricht empfangen");
Serial.print("f�r Sensor: ");
Serial.println(IncomingMessage.sensor);
Serial.print("Typ: ");
Serial.println(IncomingMessage.type);
Serial.print("Sender: ");
Serial.println(IncomingMessage.sender);
Serial.print("Command: ");
Serial.println(IncomingMessage.getCommand() + "(1 = SET, 2 = REQUEST)");
Serial.print("IsAcknowledge: ");
Serial.println(IncomingMessage.isAck());
Serial.print("Payload als String: ");
Serial.println(Payload);
Serial.print("Message data: ");
Serial.println(IncomingMessage.data);
#endif
// Eingehendes Kommando:
if (IncomingMessage.destination == MY_NODE_ID
&& IncomingMessage.getCommand() == C_SET ) //Wert fuer ein Child soll gesetzt werden!
{
Jalousien[IncomingMessage.sensor - 1].Command = IncomingMessage.type;
// �ndere Command f�r % AUF/AB auf jeweils 0%, 100% bzw. STOP:
if ( IncomingMessage.type == V_PERCENTAGE and Payload == "0") {
Jalousien[IncomingMessage.sensor - 1].Command = V_UP; //29
};
if ( IncomingMessage.type == V_PERCENTAGE and Payload == "100") {
Jalousien[IncomingMessage.sensor - 1].Command = V_DOWN; //30
};
if ( IncomingMessage.type == V_PERCENTAGE and Payload == "STOP") {
Jalousien[IncomingMessage.sensor - 1].Command = V_STOP; //31
};
//�ndere Command f�r TILT-AUF/TILT-AB jeweils mit 0%, 100% und STOP:
if ( IncomingMessage.type == V_VAR1 and ( Payload == "0" || Payload == "UP" ) )
{
Jalousien[IncomingMessage.sensor - 1].Command = V_TILT_UP; //90 TILT-UP
};
if ( IncomingMessage.type == V_VAR1 and ( Payload == "100" || Payload == "DOWN" ) )
{
Jalousien[IncomingMessage.sensor - 1].Command = V_TILT_DOWN; //91 TILT-DOWN
};
if ( IncomingMessage.type == V_VAR1 and Payload == "STOP") {
Jalousien[IncomingMessage.sensor - 1].Command = V_STOP; //31
};
//�ndere Command f�r 3xTILT-AUF/3xTILT-AB jeweils mit 0%, 100% und STOP:
if ( IncomingMessage.type == V_VAR2 and ( Payload == "0" || Payload == "UP" ) )
{
Jalousien[IncomingMessage.sensor - 1].Command = V_TILT_UP3; //92 3xTILT-UP
};
if ( IncomingMessage.type == V_VAR2 and ( Payload == "100" || Payload == "DOWN" ) )
{
Jalousien[IncomingMessage.sensor - 1].Command = V_TILT_DOWN3; //93 3xTILT-DOWN
};
if ( IncomingMessage.type == V_VAR2 and Payload == "STOP")
{
Jalousien[IncomingMessage.sensor - 1].Command = V_STOP; //31
};
#ifdef MY_DEBUG
Serial.print("Aus Message-Type abgeleitetes Jalousie-Kommando: ");
Serial.println(Jalousien[IncomingMessage.sensor - 1].Command);
Serial.println();
#endif
}
// Eingehende Anfrage:
if (IncomingMessage.destination == MY_NODE_ID
&& IncomingMessage.getCommand() == C_REQ ) //Wert fuer ein Child wird abgefragt!
{
for (byte i = 1; i <= AnzahlRaeume; i++) {
if (i == IncomingMessage.sensor)
{
if (IncomingMessage.type == V_VAR3
|| IncomingMessage.type == V_VAR4)
{
send(Jalousien[i - 1].MessageStatusAufAb.set(Jalousien[i - 1].StatusAufAb, 1), false);
send(Jalousien[i - 1].MessageStatusTilt.set(Jalousien[i - 1].StatusTilt, 1), false);
}
}
}
}
#ifdef MY_DEBUG
Serial.println("RECEIVE - END");
#endif
}