LightwaveRF Binding Updated

I’m struggling put it that way :slight_smile:

On another note, I have got a much cleaner way of running all this now, just got to write the script. I’ve also implemented a switch to update devices instead of having to rerun all the setup.

Then I need to let you have the little improvements I made here and there so you can incorporate them.
As soon as I get a bit of time on my system (not at home right now) I’ll pull off the relevant snippets of code and post them up

OK this is probably going to get messy but I’ll do this across several posts to hopefully keep it manageable.

First off, here’s my authentication rule:

//LightWave_Rule_Authentication
rule “LightWave Authentication”
when
System started or Time cron “30 0 0 1/1 * ? *” or Item Authentication_Switch changed from OFF to ON
// 30 secs past midnight daily or when Authentication_switch is set to ON
then
var String username = “REDACTED”
var String password = “REDACTED”
logInfo(“lightwave.rules”, “Authentication rule ran”)
val Get_Token = executeCommandLine(“curl@@-X@@POST@@https://auth.lightwaverf.com/v2/lightwaverf/autouserlogin/lwapps@@-H@@Content-Type: application/json@@-H@@x-lwrf-appid: ios-01@@-d@@{“email” : “” + username + “”,“password” : “” + password + “”}”,5000)
var String RefreshToken = transform(“JSONPATH”, “$.refresh_token” , “{” + Get_Token.substringBetween("“tokens”:{","}}") + “}”)
var String AccessToken = transform(“JSONPATH”, “$.access_token” , “{” + Get_Token.substringBetween("“tokens”:{","}}") + “}”)
Refresh_Token.postUpdate(RefreshToken)
Access_Token.postUpdate(AccessToken)
Thread::sleep(3000)
Authentication_Switch.sendCommand(OFF)
end

So basically I added an Authentication_Switch so that I can fire the authentication whenever I want. I can do it manually, I have the switch in my Sitemap, or I can do it automatically from the polling rule whenever it detects that the response contains UNAUTHORISED. That lets me automatically recover from the tokens being expired or messed up for any reason

There’s also this line

logInfo(“lightwave.rules”, “Authentication rule ran”)

which throws a custom entry into the logs so I can record when it ran

More to follow:

Here are some sections from my items file. This is a Gen 2 dimmer:

Switch Lightwave_BathroomLights_switch “Bathroom Lights” (GroupGroundSockets, FF_Bathroom)
Dimmer Lightwave_BathroomLights_dimLevel “Bathroom Lights dimLevel” (GroupGroundLights, FF_Bathroom)
String Lightwave_BathroomLights_dimSetup "Bathroom Lights dimSetup "
Switch Lightwave_BathroomLights_protection “Bathroom Lights protection” (GroupGroundSocketsOther, FF_Bathroom)
Number Lightwave_BathroomLights_power “Bathroom Lights power [%.1f W] (Read Only)” (GroupGroundSocketsPower, FF_Bathroom)
Number Lightwave_BathroomLights_energy “Bathroom Lights energy [%.2f kWh] (Read Only)” (GroupGroundSocketsEnergy, FF_Bathroom)
String Lightwave_BathroomLights_bulbSetup "Bathroom Lights bulbSetup "
Number Lightwave_BathroomLights_Power_Cost “Bathroom Lights Power Cost [%.3f £/Hour]” (GroupGroundSocketsPowerCost, FF_Bathroom)
Number Lightwave_BathroomLights_Energy_Cost “Bathroom Lights Energy Cost [£ %.2f to date]” (GroupGroundSocketsEnergyCost, FF_Bathroom)
Switch Lightwave_BedroomLight_identify “Bedroom Light identify” (GroupGroundSocketsOther)
Switch Lightwave_BedroomLight_reset “Bedroom Light reset” (GroupGroundSocketsOther)
Switch Lightwave_BedroomLight_upgrade “Bedroom Light upgrade” (GroupGroundSocketsOther)
Number Lightwave_BedroomLight_diagnostics "Bedroom Light diagnostics "

I worked out the proper formatting and labelling for energy and power and I sorted out the icons. I’m using the item type Dimmer for the dim level as it’s easy to work with.

This is a Gen 2 socket:

Switch Lightwave_Internet_switch “Internet” (GroupGroundSockets, GF_LivingRoom)
String Lightwave_Internet_outletInUse “Internet outletInUse [%s] (Read Only)” <poweroutlet_uk> (GF_LivingRoom)
Switch Lightwave_Internet_protection “Internet protection” (GroupGroundSocketsOther, GF_LivingRoom)
Number Lightwave_Internet_power “Internet power [%.1f W] (Read Only)” (GroupGroundSocketsPower, GF_LivingRoom)
Number Lightwave_Internet_energy “Internet energy [%.2f kWh] (Read Only)” (GroupGroundSocketsEnergy, GF_LivingRoom)
Switch Lightwave_Internet_identify “Internet identify” (GroupGroundSocketsOther)
Switch Lightwave_Internet_reset “Internet reset” (GroupGroundSocketsOther)
Switch Lightwave_Internet_upgrade “Internet upgrade” (GroupGroundSocketsOther)
Number Lightwave_Internet_diagnostics "Internet diagnostics "
String Lightwave_Internet_periodOfBroadcast "Internet periodOfBroadcast "
Number Lightwave_Internet_rgbColor "Internet rgbColor "
Number Lightwave_Internet_voltage “Internet voltage [%.1f V] (Read Only)” (GF_LivingRoom)
Number Lightwave_Internet_current “Internet current [%.1f A] (Read Only)” (GF_LivingRoom)
Number Lightwave_Internet_Power_Cost “Internet Power Cost [%.3f £/Hour]” (GroupGroundSocketsPowerCost, GF_LivingRoom)
Number Lightwave_Internet_Energy_Cost “Internet Energy Cost [£ %.2f to date]” (GroupGroundSocketsEnergyCost, GF_LivingRoom)

Pesky forum now won’t let me post anymore so I’ll have to keep editing this thread and turning it into a monster (which I wanted to avoid). Ah well.

On to the rules then. First, the switch rules. When I first set it up the dimlevel rule wasn’t working so I modified it:

//Hall Lights dimLevel Rules
rule “Hall Lights dimLevel Rule” when Item Lightwave_HallLights_dimLevel received command then
var String Access_Token = Access_Token.state.toString
executeCommandLine("curl@@-g@@-XPOST@@https://publicapi.lightwaverf.com/v1/feature/5aXXXXXXXXXXXXX6-59-3157328472+0@@-H@@Content-Type: application/json@@-H@@Authorization:bearer " + Access_Token + “@@-d@@{
“value” : “” + Lightwave_HallLights_dimLevel.state.toString + “”}”,5000)
end

Refresh rule next. As I mentioned before I removed everything I don’t actually need to pull back and just kept it to the ones I specifically need to automate things and to indicate the states in the sitemap. This also reduces the load on the Pi, makes the rule run through a bit faster and reduces the logging frequency. On that subject I’m using the Pi with an external SSD but if it was just the SD card it would beat it to death to log, for example, the LinkPlus date and time because that’s different every single time it polls.

I also changed the cron for the poll to once every 30 secs. Device state changes, via the switch rule, go pretty much instantly so the polling is only to reflect the state for any further automation logic and for my mimic in the sitemap. You might want it faster but I’ve lived with 30sec for ages now and it does me fine.

On to some specifics in the refresh rule. This bit pulls the Lighwave Link Dusk (and dawn) time as a proper time and sorts out the leading zeroes to make it display properly:

//LinkPlus DuskTime
var Lightwave_LinkPlus_duskTime_Value = transform(“JSONPATH”,"$.5a6XXXXXXXXXXX8f26-10-3157328472+0", Readme)
var int DuskTotalMinutes = Integer::parseInt(Lightwave_LinkPlus_duskTime_Value)/60
var int DuskRemainderSecs = Integer::parseInt(Lightwave_LinkPlus_duskTime_Value)%60
var int DuskTotalHours = DuskTotalMinutes/60
var int DuskRemainderMins = DuskTotalMinutes%60
var String DuskHoursPad = ‘’
var String DuskMinsPad = ‘’
var String DuskSecsPad = ‘’
if (DuskTotalHours < 10) { DuskHoursPad = ‘0’ }
if ( DuskRemainderMins < 10 ) { DuskMinsPad = ‘0’ }
if ( DuskRemainderSecs < 10 ) { DuskSecsPad = ‘0’ }
val String LinkPlus_DuskTime_String = DuskHoursPad + DuskTotalHours.toString + “:” + DuskMinsPad + DuskRemainderMins.toString + “:” + DuskSecsPad + DuskRemainderSecs.toString
Lightwave_LinkPlus_duskTime.postUpdate(LinkPlus_DuskTime_String)

Here’s some parts of the rule block for a Gen 2 socket:

// Internet Socket

var Lightwave_Internet_switch_Value = transform(“JSONPATH”,"$.5a6XXXXXXXXX98f26-32-3157328472+0", Readme)
Lightwave_Internet_switch.postUpdate(if(Lightwave_Internet_switch_Value == “1”){ON} else if (Lightwave_Internet_switch_Value == “0”){OFF})

var Lightwave_Internet_outletInUse_Value = transform(“JSONPATH”,"$.5a6aXXXXXXXXXf98f26-33-3157328472+0", Readme)
if (Lightwave_Internet_outletInUse_Value == “1”) {
Lightwave_Internet_outletInUse.postUpdate(“YES”)
}
else if (Lightwave_Internet_outletInUse_Value == “0”) {
Lightwave_Internet_outletInUse.postUpdate(“NO”)
}
else {
Lightwave_Internet_outletInUse.postUpdate(Lightwave_Internet_outletInUse_Value)
}

var Lightwave_Internet_protection_Value = transform(“JSONPATH”,"$.5a6XXXXXXXXX98f26-34-3157328472+0", Readme)
Lightwave_Internet_protection.postUpdate(if(Lightwave_Internet_protection_Value == “1”){ON} else if (Lightwave_Internet_protection_Value == “0”){OFF})

var Lightwave_Internet_power_Raw = transform(“JSONPATH”,"$.5a6aXXXXXXXXXXf98f26-35-3157328472+0", Readme)
var Lightwave_Internet_power_Value = Float::parseFloat(Lightwave_Internet_power_Raw)
Lightwave_Internet_power.postUpdate(Lightwave_Internet_power_Value)
Lightwave_Internet_Power_Cost.postUpdate(Float::parseFloat(Lightwave_Internet_power_Raw) / 1000 * Electricity_Cost)

var Lightwave_Internet_energy_Value = transform(“JSONPATH”,"$.5a6XXXXXXXXX8f26-36-3157328472+0", Readme)
Lightwave_Internet_energy.postUpdate(Float::parseFloat(Lightwave_Internet_energy_Value) / 1000)
Lightwave_Internet_Energy_Cost.postUpdate(Float::parseFloat(Lightwave_Internet_energy_Value) / 1000 * Electricity_Cost)

var Lightwave_Internet_voltage_Raw = transform(“JSONPATH”,"$.5a6XXXXXXXXXX8f26-31-3157328472+0", Readme)
var float Lightwave_Internet_voltage_Value = Float::parseFloat(Lightwave_Internet_voltage_Raw)/10
Lightwave_Internet_voltage.postUpdate(Lightwave_Internet_voltage_Value)
var Lightwave_Internet_current_Value = Lightwave_Internet_power_Value/Lightwave_Internet_voltage_Value
Lightwave_Internet_current.postUpdate(Lightwave_Internet_current_Value)

Here you can see I converted the 1 and 0 results for Outlet In Use to YES and NO. The way you’ve done the switch status is neater though. There’s also the modified logic to calculate the power and energy properly and I also derive the current. I’ve used similar logic for all the other devices.

In case anybody has one, here’s the code block for the energy monitor:

// Energy Monitor
var Lightwave_EnergyMonitor_power_Raw = transform(“JSONPATH”,"$.5a6XXXXXXXXXX98f26-146-3157328472+0", Readme)
var Lightwave_EnergyMonitor_power_Value = Float::parseFloat(Lightwave_EnergyMonitor_power_Raw) / 1000
Lightwave_EnergyMonitor_power.postUpdate(Lightwave_EnergyMonitor_power_Value)
Lightwave_EnergyMonitor_Power_Cost.postUpdate(Lightwave_EnergyMonitor_power_Value * Electricity_Cost)
var Lightwave_EnergyMonitor_current_Value = Lightwave_EnergyMonitor_power_Value * 1000 / Lightwave_Internet_voltage_Value
Lightwave_EnergyMonitor_current.postUpdate(Lightwave_EnergyMonitor_current_Value)
var Lightwave_EnergyMonitor_energy_Value = transform(“JSONPATH”,"$.5a6XXXXXXXX8f26-147-3157328472+0", Readme)
Lightwave_EnergyMonitor_energy.postUpdate(Float::parseFloat(Lightwave_EnergyMonitor_energy_Value) / 1000)
Lightwave_EnergyMonitor_Energy_Cost.postUpdate(Float::parseFloat(Lightwave_EnergyMonitor_energy_Value) / 1000 * Electricity_Cost)
var Lightwave_EnergyMonitor_rssi_Value = transform(“JSONPATH”,"$.5a6XXXXXXXXX98f26-148-3157328472+0", Readme)
Lightwave_EnergyMonitor_rssi.postUpdate(Lightwave_EnergyMonitor_rssi_Value)
var Lightwave_EnergyMonitor_identify_Value = transform(“JSONPATH”,"$.5a6XXXXXXXXXXXf26-149-3157328472+0", Readme)
Lightwave_EnergyMonitor_identify.postUpdate(if(Lightwave_EnergyMonitor_identify_Value == “1”){ON} else if (Lightwave_EnergyMonitor_identify_Value == “0”){OFF})

Alongside the provided rule files I also added a Timed Events rule to trigger timed events. I found my Lightwave Link sometimes choked when I hurled lots of changes at it all at once so I staggered them:

// Turn Indoor Lights ON at 19:00 (DUSK might have already done it)
rule “ON at 19:00”
when
Time cron “0 00 19 * * ?” //19:00 Daily
then
logInfo(“lightwave.rules”, “Indoor Lights ON at 19:00”)
Lightwave_GreenLamp_switch.sendCommand(ON)
Thread::sleep(3000)
Lightwave_LavaLamp_switch.sendCommand(ON)
Thread::sleep(3000)
Lightwave_WindowLights_switch.sendCommand(ON)
Thread::sleep(3000)
Lightwave_Twigs_switch.sendCommand(ON)
Thread::sleep(3000)
Lightwave_ReadingLamp_switch.sendCommand(ON)
Thread::sleep(3000)
Lightwave_HallLights_dimLevel.sendCommand(80)
Thread::sleep(3000)
Lightwave_HallLights_switch.sendCommand(ON)
sendNotification(“alandrury@gmail.com”, “Indoor Lights ON at 19:00”)
end

The sendNotification line there sends a push notification to my Android phone via the OpenHAB cloud connector. I have that for several events.

Aaaaand finally, in the same rules file, I wrote some Wakeup Light code:

// Wake Up Light
rule “Wake Up Light”
when
Time cron “0 40 06 * * ?” //Starts at 06:40 Daily, if enabled
then
if (WakeUp_Light_Alan.state == ON) {
logInfo(“lightwave.rules”, “Alan’s Wake Up Light Sequence Started”)
sendNotification(“alandrury@gmail.com”, “Alan’s Wake Up Light Sequence Started”)
val int Alan_Wakeup_StartLevel = 10
val int Alan_Wakeup_EndLevel = 81
Lightwave_BedroomLight_dimLevel.sendCommand(Alan_Wakeup_StartLevel)
Thread::sleep(3000)
Lightwave_BedroomLight_switch.sendCommand(ON)
Thread::sleep(25000)
var int ia = Alan_Wakeup_StartLevel
while ((ia=ia+1) < Alan_Wakeup_EndLevel) {
sendCommand(Lightwave_BedroomLight_dimLevel, ia)
Thread::sleep(25000)
}
sendNotification(“alandrury@gmail.com”, “Alan’s Wake Up Light Sequence Ended”)
logInfo(“lightwave.rules”, “Alan’s Wake Up Light Sequence Ended”)
}
end

If you’re still awake after all that, feel free to reuse any of the above

@Fixer had a quick skim, couple bits in there at least I’ll integrate from first glance, some bits won’t apply now either where I’ve merged to exec v2. I have it all working now on the v2, just a few little niggles I’m trying to eradicate (that were there already) and then I’ll get it up.
Have tidied up the code somewhat and also changed the methods of some of it.
Cheers for the data though will be helpful

got rid of freaky switches aswell:

@Fixer @anon99588902
BACKUP what you have already, remove it from openhab and you can try this with the v2 exec binding instead of the v1.

instructions are at the top of the rules file.
Let me have some feedback if pos.
Ive integrated a couple of bits from you files at present @Fixer, couple more bits to get in.
Dont forget to change the paths at the top as your both running different systems to me now.

Lw_Base_2019-10-25.rules.txt (25.7 KB) Lw_Setup_2019-10-25.items.txt (32 Bytes)

1 Like

Cool! I’ll give it a go tomorrow.

In the meantime I realised I forgot the bit of logic where I test the ‘Readme’ that comes back from the curl query. Here it is:

… do the curl query that returns Readme …
// Check for retrieval of data
if (Readme == “”) {
logError(“lightwave.rules”, “Lightwave Refresh FAILED! - no data returned”)
return;
}
if (Readme.contains(“Unauthorized”)) {
logError(“lightwave.rules”, “Lightwave Refresh FAILED! - authentication failure”)
Authentication_Switch.sendCommand(ON)
Thread::sleep(30000)
return;
}
else {
logInfo(“lightwave.rules”, “Lightwave Refresh OK”)
}
… then continue with the rest of the processing …

If it get null return I just bomb out of this execution to avoid all the consequent errors. If I get Unauthorized I throw the switch to run the auth rule. If I get neither of those I carry on and do all the subsequent processing

I thought you had :joy:
I’m going to add in still:
Current Item
Error handling
Dawn/dusk
Going to add some logic in for groups so people can automate from setup based on item names

I’ve already added:
Icon for in use
Transition for in use
Correct formats for energy/power

Plus added:
Complete conversion to v2 exec (had to use some scripts due to a bug I’ve reported)
Changed rules to get rid of variables
Added locks to get rid of race condition icon flapping (you’ll have to experiment with timings if your going to stay at 30 seconds)

Oh and rather than cron for auth it’s now in the auth thing - set at 1 hour at the moment (3600) so you may want to change to 86400 to make it 24hours.

I think I’m close to writing an actual binding aswell (using others as templates) - prob not gonna be for a few months though.

Love the advertising of the ‘Green Lava Lamp’ again btw :stuck_out_tongue_winking_eye:

Hopefully not too many bugs to report back
And you could have just pm’d me your files (less the user details) to save the monster, and it’d be easier to read

Hi Dave

Great work…

OK found an error…Line 76 to 81 /share/QPKGs/ hard path

//Define Things File
var String RulesFile = “Thing exec:command:LwAuth [ command=\”%2\$s\", interval=3600,timeout=5, autorun=true ]\r\n" +
“Thing exec:command:LwGet [ command=\“sh /share/QPKGs/OpenHab/conf/scripts/Lw_Get.sh %2\$s\”, interval=” + RefreshRate + “,timeout=10, autorun=true ]\r\n” +
“Thing exec:command:LwUpdate_Structures [ command=\“sh /share/QPKGs/OpenHab/conf/scripts/Lw_Update_Structures.sh %2\$s\”, interval=0,timeout=1000, autorun=true ]\r\n” +
“Thing exec:command:LwUpdate_Features [ command=\“sh /share/QPKGs/OpenHab/conf/scripts/Lw_Update_Features.sh %2\$s\”, interval=0,timeout=1000, autorun=true ]\r\n” +
“Thing exec:command:LwUpdate [ command=\“sh /share/QPKGs/OpenHab/conf/scripts/Lw_Update.sh %2\$s\”, interval=0,timeout=1000, autorun=true ]\r\n”

If I edit the paths

in my case

/openhab/conf/scripts/

it works perfectly

:grin:

OK I have edited your script with " + OpenHabScriptsPath + "

Lw_Base_2019-10-25_2.rules.txt (25.5 KB)

1 Like

Missed that one :+1:

Hi Dave,

This is working really well… No issues - even had a power outage tonight (longer than the UPS could cope with :frowning:) any way… the LightwaveRF came right up seemlessly and just worked.

Just want to say thank you very much for this… I will be rolling out alot more Lightwave

1 Like

@anon99588902 cheers. makes it all worth while when others are getting use from it. It’s not perfect but fills a gap in the addons.

Hopefully will get chance to try it tonight. Life’s been manic

I have it on openhab 2.5 latest snapshot installed dockerized on raspberry pi and is working very well! Apparently no issues, refresh rate 6sec, currently only 3 smart sockets(gen2) but i’ll add more devices.

1 Like

Hi

Will this only run on Linux or Mac based systems as my current installation in windows based?

Thanks

Hmmm… you got me :joy:

I haven’t tested on windows but I think the scripts would have to be changed as you are going to have to run cmd to then invoke curl.

I’ll have a look when I have time but work is busy at present so may not be for a good few weeks

@delid4ve

Yes I looked through the scripts and that’s what made me think it seems Linux based, it’s not an issue I can change OS to Linux as the main reason I want Openhab is for this integration along with a few others.

Thanks

Hi

I moved over to Linux OS and have openhab running and all my other devices working i have followed the steps above and installed EXEC binding and the Regex transformation in the paperui but i have the following errors in the log and also within the control section of the paperui the text just fills the entire screen, What am i missing?

2019-11-08 16:26:35.752 [WARN ] [hab.binding.exec.handler.ExecHandler] - Couldn’t transform response because transformationService of type ‘REGEX’ is unavailable
2019-11-08 16:26:41.758 [WARN ] [hab.binding.exec.handler.ExecHandler] - Couldn’t transform response because transformationService of type ‘REGEX’ is unavailable

Thanks

it looks like you need to install the RegEx Transformation binding on your openhab system