DSC Binding - how to send a command string similar to keypad

@rsstephens thank you for comprehensive explanation. I removed the 1.7 DSC addon and copied 1.8 DSC Alarm Binding and DSC Alarm Action binding. Restarted Openhab and all seems to work. Except one thing the same was with the 1.7 setup to. I send “Set time/date” command
Panel message reads
500: A command has been received successfully.
502: An error has been detected
And I get Panel system error:
011: Keybus Transmit Time Timeout

Arm, Disarm functions, I get the sensor openings to. this is the only “bug” that I found… Is this known?

@JjS had this same issue also. I don’t have any idea why it returns that error. As stated earlier I run openHAB on a Windows system and I can’t seem to duplicate that error when sending the “Set time/date” command. Maybe @JjS found a solution.

My workaround, a rule run once a day, sending individual keystrokes. Enough to compensate the time drift of the control panel. I was not able to send it at once with 071 as the payload was to long. BTW: Is it possible to change this limit in the action to accept ‘*6code1HHMMmmDDYY’ or at least the date in one shot (10 bytes)?

Change 1234 sample code to master code of your panel:

import org.openhab.core.library.types.*     
import java.util.Date     
import java.text.SimpleDateFormat 
rule "dscSetTime"
when 
Time cron "0 10 21 ? * *"
then
var SimpleDateFormat df = new SimpleDateFormat( "HHmmMMddYY" )
var String Timestamp = df.format( new Date() )             
logInfo( "DSC", "Settime to:{}", Timestamp )
logInfo( "DSC", "Settime sending *")
sendDSCAlarmCommand('070','*')
Thread::sleep(1000)

logInfo( "DSC", " Settime sending 6")
sendDSCAlarmCommand('070','6')
Thread::sleep(1000)

logInfo( "DSC", " Settime sending c")
sendDSCAlarmCommand('070','1')
Thread::sleep(1000)

logInfo( "DSC", " Settime sending o")
sendDSCAlarmCommand('070','2')
Thread::sleep(1000)

logInfo( "DSC", " Settime sending d")
sendDSCAlarmCommand('070','3')
Thread::sleep(1000)

logInfo( "DSC", " Settime sending e")
sendDSCAlarmCommand('070','4')
Thread::sleep(2000)

logInfo( "DSC", " Settime sending 1")
sendDSCAlarmCommand('070','1')
Thread::sleep(1000)

logInfo( "DSC", " Settime sending H1:{}",Timestamp.charAt(0))
sendDSCAlarmCommand('070',Timestamp.charAt(0).toString)
Thread::sleep(1000)

logInfo( "DSC", " Settime sending H2:{}",Timestamp.charAt(1))
sendDSCAlarmCommand('070',Timestamp.charAt(1).toString)
Thread::sleep(1000)

logInfo( "DSC", " Settime sending m1:{}",Timestamp.charAt(2))
sendDSCAlarmCommand('070',Timestamp.charAt(2).toString)
Thread::sleep(1000)

logInfo( "DSC", " Settime sending m2:{}",Timestamp.charAt(3))
sendDSCAlarmCommand('070',Timestamp.charAt(3).toString)
Thread::sleep(1000)

logInfo( "DSC", " Settime sending M1:{}",Timestamp.charAt(4))
sendDSCAlarmCommand('070',Timestamp.charAt(4).toString)
Thread::sleep(1000)

logInfo( "DSC", " Settime sending M2:{}",Timestamp.charAt(5))
sendDSCAlarmCommand('070',Timestamp.charAt(5).toString)
Thread::sleep(1000)

logInfo( "DSC", " Settime sending D1:{}",Timestamp.charAt(6))
sendDSCAlarmCommand('070',Timestamp.charAt(6).toString)
Thread::sleep(1000)

logInfo( "DSC", "Settime sending D2:{}",Timestamp.charAt(7))
sendDSCAlarmCommand('070',Timestamp.charAt(7).toString)
Thread::sleep(1000)

logInfo( "DSC", "Settime sending Y1:{}",Timestamp.charAt(8))
sendDSCAlarmCommand('070',Timestamp.charAt(8).toString)
Thread::sleep(1000)

logInfo( "DSC", "Settime sending Y2:{}",Timestamp.charAt(9))
sendDSCAlarmCommand('070',Timestamp.charAt(9).toString)
Thread::sleep(2000)

logInfo( "DSC", "Settime sending #")
sendDSCAlarmCommand('070','#')

logInfo( "DSC", "Settime done")

end
1 Like

Hello @JjS,

Great workaround. I am glad to see you got a solution to this issue. Thanks for your response.

BTW: Is it possible to change this limit in the action to accept ‘*6code1HHMMmmDDYY’ or at least the date in one shot (10 bytes)?

I don’t think it will be possible, sorry. The limitation is built into the binding API itself which is actually based on the Envisalink’s keystroke limit for the ‘071’ command. It is a pre-check the API does before sending the command and logs an error if the keystroke length is over 6. All the action does is pass the command and info to the binding API, therefore it needs to comply with the limitations in order to work. It would be nice if the Envisalink allowed more keystrokes in the command, unfortunately it doesn’t. I guess they have their reasons for the limit.

This is a good workaround. I was doing something similar with my Haiku system setting my Omnipro security/automation board daily to keep everything synced with NTP.

Works for me too. I had to add another couple of digit transmissions as I’m using 6 digit codes on my system. But that worked as expected.

Don’t suppose you know whether the panel will tolerate this time update while it’s armed? Or will it go into alarm?! i.e. should I be checking to see if it’s armed before sending this?

Thanks!

I can also confirm sending arming codes too.

rule "DSC ARM"
when
	Item armAlarm changed
then
	sendDSCAlarmCommand("030", "1")

Works like a charm, Away arming the system.

Hi @rsstephens,

I am working on getting the DSC action working. I have an IT100 over TCP trying to send the Single Keystroke command (070).

From IT100 developer’s manual, it states that “To accomodate this requirement, each simulated Keypress (070, D, XX, CR/LF) command must be followed by a keybreak (070, ^, F5, CR/LF)”

When I try to send the sendDSCAlarmCommand(“070”,"^"), I got

sendCommand(): ‘keystroke’ must be a single character string from 0 to 9, *, #, or A, it was: ^

Just wondering would the IT100 still take the keystroke when not keybreak was sent?

Thanks,
Joe

Hello @goodfore,

Unfortunately, at this point, the ‘keystroke’ internal API command doesn’t support all the characters for the IT100, which includes ‘^’ key break character. The binding was originally built around the Envisalink TPI protocol, although similar to the IT100 API, it has its differences this being one of them. The ‘keystroke’ command for the Envisalink only allows for the characters 0-9, *, #, and A, and that’s why you are seeing that message. You can try it with out the ‘^’ character but I’m not sure how that will turn out.

Incorporating the extra characters for the IT100 will require a programming change, and looking at the specifications for the IT100, the ‘keybreak’ character requires at least a 1.5 second delay before sending it after the previous character. So it is a little more complicated than just allowing for those characters to pass. I will look into making the changes, but not sure how long it will be. Sorry for the inconvenience.

Hi @rsstephens,

Thank you for the swift response.

I have tried sending the command over without the key break (^), it doesn’t seem to work. I think the IT 100 needs that key break to work.

Looking over the spec, I think the 1.5 second delay is only required if we are trying to send in a long press. Otherwise, the key break can be sent immediately after the key code.

Looking thru the code, with my limited knowledge of openhab source, I think we can modify the 070 case inside openhab/bundles/binding/org.openhab.binding.dscalarm/src/main/java/org/openhab/binding/dscalarm/internal/protocol/API.java

case KeyStroke: /* 070 */
        if (interfaceType.equals(DSCAlarmInterfaceType.ENVISALINK)) {
                    if (apiData[0] == null || apiData[0].length() != 1 || !apiData[0].matches("[0-9]|A|#|\\*")) {
                        logger.error("sendCommand(): \'keystroke\' must be a single character string from 0 to 9, *, #, or A, it was: {}", apiData[0]);
                        break;
                    }
        } else if (interfaceType.equals(DSCAlarmInterfaceType.IT100)) {
                    if (apiData[0] == null || apiData[0].length() != 1 || !apiData[0].matches("[0-9]|\\^|#|\\*")) {
                        logger.error("sendCommand(): \'keystroke\' must be a single character string from 0 to 9, *, #, or ^, it was: {}", apiData[0]);
                        break;
                    }
        } else {
                     break;
        }
        data = apiData[0];
        validCommand = true;
        break;

Not sure if the ^ needs escape. Not the nicest code nor the complete, but I think this can get us started.
I don’t have an IDE setup, if someone can compile a jar, I can test it.

Thanks

Hello @goodfore,

Your exactly right the delay is only required for a long key press, and what you have there would work. I think you do have to escape the ^ character as it is a special character in regular expressions. I don’t have the IDE setup at the moment either, at least it’s not current. I might be able to get to it this weekend and have you a compiled jar to test.

Hi @rsstephens,

Thank you so much for your help. No rush, whenever you are available.

Hello @goodfore,

I have a .jar file you can test located here. I instituted your code plus I allowed for all keystrokes according to the documentation. Also, I put in the possibility to perform a long key press by implementing a slight delay when the character ‘L’ is received through the sendDSCAlarmCommand (ex. sendDSCAlarmCommand(“070”, “L”), not sure how it is going to work. This will only work for the IT100 and you would need to follow it with the key break character ‘^’. Try it out let us know how it goes. Thanks.

Hi @rsstephens,

Thank you very much for putting the jar together.

I have tried the jar. I tried the “^” key and function 3 (“c”), both seems to be wrong great. Also the 1-9, * and # is working as they should.

I also try to use the special long press key, (“L”), but can’t get it to work.

I tried
sendDSCAlarmCommand(“070”,“c”)
sendDSCAlarmCommand(“070”,“L”)
sendDSCAlarmCommand(“070”,"^")

Doesn’t do anything, but if I do

        sendDSCAlarmCommand("070","c")
        Thread::sleep(1500)
        sendDSCAlarmCommand("070","^")

It does what it supposes to do.

Thanks

Hello @goodfore,

Thank you for testing this. I’m glad it is mostly working. To bad the long key press delay isn’t working. I’m thinking it is because there are separate threads running and the delay in the binding has no effect on the rule. Oh well it was a good try. It seems to work when the delay is in the rule itself. Maybe that is the way to do it at this point. Thanks.

Hello @goodfore,

I figured out why the long key press delay wasn’t working. If you are still willing to test the new .jar file is located here. Now, instead of sending a break key (’^’) after the long key press (‘L’), just send the long key press. So, you would only need to have the following in the rule:

sendDSCAlarmCommand(“070”,“c”)
sendDSCAlarmCommand(“070”,“L”)

I know you can get the same results in the rule, but it is just another way of doing it. I appreciate your feedback. Hope this helps. Thank you.

Hi @rsstephens,

Thanks for looking this. I gave the new jar a try. It’s working as expected.

I finally got the IDE setup and working on some issues I found with the IT100.

  1. the zone bypass status doesn’t seem to work
  2. the “040”, disarm command doesn’t seem to work either.

For issue #1, I found out that IT 100 only send zone bypass status update if the zone is bypassed by the IT100. For example if I send a “*108#” using the 070 command (sending the characters individually), IT 100 will send a 901 update with message along the line of “Zone 08 B”, with B being bypassed and O being armed.

I tweaked the code a bit for DSCAlarmActiveBinding/dscAlarmEventRecieved adding

case LCDUpdate: /* 901 */
                if (interfaceType.equals(DSCAlarmInterfaceType.IT100)) {

                    if (apiData.substring(5, 9).equals("Zone")) {
                        logger.debug(
                                "dscAlarmEventRecieved(): Event 901 received, zone status update detected! Looking for zone: {}",
                                dscAlarmItemType);
                        int aZone = Integer.parseInt(apiData.substring(13, 15));
                        if (String.valueOf(apiData.charAt(apiData.length() - 1)).equals("O")) {
                            updateItemType(DSCAlarmItemType.ZONE_BYPASS_MODE, 1, aZone, 0);
                        } else if (String.valueOf(apiData.charAt(apiData.length() - 1)).equals("B")) {
                            updateItemType(DSCAlarmItemType.ZONE_BYPASS_MODE, 1, aZone, 1);
                        }
                    }
                }
                break;

As you can tell, there is no way to tell which partition the zone is in from the message, so I don’t this solution is good enough for general use.

For issue #2, I have not start looking into it. Would you have any insight on why it isn’t working?

Thanks

Hi @rsstephens ,

I think I might know why command 040 doesn’t work for IT 100.

case PartitionDisarmControl: /* 040 */
                if (apiData[0] == null || !apiData[0].matches("[1-8]")) {
                    logger.error(
                            "sendCommand(): Partition number must be a single character string from 1 to 8, it was: {}",
                            apiData[0]);
                    break;
                }

                if (dscAlarmUserCode == null || dscAlarmUserCode.length() < 4 || dscAlarmUserCode.length() > 6) {
                    logger.error("sendCommand(): User Code is invalid, must be between 4 and 6 chars: {}",
                            dscAlarmUserCode);
                    break;
                }

                if (!interfaceType.equals(DSCAlarmInterfaceType.IT100)) {
                    data = apiData[0] + String.format("%-6s", dscAlarmUserCode).replace(' ', '0');
                } else {
                    data = apiData[0] + dscAlarmUserCode;
                }
                validCommand = true;
                break;

In the last If statement, I think the IT 100 also need padding of 0 in the 040 command, maybe we can change it to just pad 0 for both IT 100 and Envisalink?

Thanks

Hello @goodfore,

Thanks for testing that last change. Looking at the code for the 040 command I think there is a bug in the logic. The negate operator (!) shouldn’t be in the ‘if’ statement for the 040 command. The It100 requires the 6 digit user codes, but the Envisalink doesn’t. Try changing that to see how it works.

Looks good on the 901 message code. I would remove the if(interfaceType.equals(DSCAlarmInterfaceType.IT100)) statement. The IT100 will be the only interface to send that message code because the Envisalink does not include that message code in it’s protocol. I wish the Envisalink did have a way to inform about a zone in bypass mode, but it doesn’t at this point. Good work.

Hi @rsstephens,

Been busy lately, so didn’t get to update you sooner.

I have changed the 040 command by dropping the negate operator (!). Everything works wonderfully.

Can you make that change in your code and do a PR, along with your update to the 070 command? I haven’t really done a PR before, so don’t know how it works yet.

Thanks.