Jump to content

Welcome to Smart Home Forum by FIBARO

Dear Guest,

 

as you can notice parts of Smart Home Forum by FIBARO is not available for you. You have to register in order to view all content and post in our community. Don't worry! Registration is a simple free process that requires minimal information for you to sign up. Become a part of of Smart Home Forum by FIBARO by creating an account.

 

As a member you can:

  •     Start new topics and reply to others
  •     Follow topics and users to get email updates
  •     Get your own profile page and make new friends
  •     Send personal messages
  •     ... and learn a lot about our system!

 

Regards,

Smart Home Forum by FIBARO Team


Recommended Posts

  • Topic Author
  • Working with event runner rules it can be useful to understand a bit how they work behind the "hood".

    Rules are always on the form

    Please login or register to see this code.

    Ex

    Please login or register to see this code.

    If there is no "=>" in the rule it is considered just an expression that is executed immediately.

    When the rule is defined (the function rule(<str>) executes) the string is compiled to an internal efficient way to run the rule.

    The rules are usually defined within a main() function that is run once. The definition/compilation stores the rules in a rule database to be triggered when appropriate events happens. It means that in main() is also a good place to run things that should only be run once when EventRunner starts up. Like initialising  device to off or setting ups Lua variables etc.

     

    At "compile time" the test part - the left-hand side - is examined for tests that involved devices, events and globals - referred to as "triggers".

    Ex. the above rule contains a reference to device 88 and a breached test (which is the same as a 'value' property change)

     

    The list of triggers for a rule is used to determine when a rule should run. The above rule will only be run when we get a system event that device 88's value property has changed,

    Please login or register to see this code.

    This will trigger when 88's lux property change or the fibaro global MyDay changes value (then it will test if  $MyDay=='work')

    If the examination of the test can't find any triggers it will give the error "no triggers found in rule ..."

     

    The effect of this is that rules will only run when a trigger is coming in that could actually trigger the rule.

    This is very efficient as we don't have to run through all the rules every time - because that would mean that the system became slower the more rules we added.

    Instead, this allows us to scale and manage a lot of rules.

    If it triggers the rule we run the test part to see if the condition is full-filled and in that case we run the right-hand side, the actions..

    So, a rule can still be triggered but the test being false - we can't get around that.

     

    There is another trigger that is the 'time triggers'. @<expr> and <exprA>..<exprB> causes the rule to be triggered at given times.

    The <exprA>..<exprB> is the interval test. Ex sunrise-00:10..sunset+00:10

    It causes the rule to be triggered at time <exprA> and at time <exprB>+1s

    Ex.

    Please login or register to see this code.

    We want this rule to trigger if 88 changes value. We also want it to trigger at sunrise. Because the 88 can have been breached just before sunrise but the rule didn't execute because it was before sunset. Then when sunrise happens we want the rule to run (if 88 is still breached of course). The reason we also trigger the rule at <exprB>+1s is because we want negations to work too

    Please login or register to see this code.

    ! is the logical  'not' operator - it requires a bit of thinking to understand why it needs to test x+1s, but the important is that it makes the rules easy and logical to write.

     

    The last type of trigger is events. Events are Lua tables with a type field.

    Please login or register to see this code.

    The above is a typical sourceTrigger on the HC2 (on the HC3 the fields are a bit different but still has a type field)

    An event trigger in a rule can be written

    Please login or register to see this code.

    and it will trigger the rule when an incoming trigger matching the event is seen. It can of course be combined with other triggers described above.

    However, the above rule is exactly as this rule

    Please login or register to see this code.

    The compiler translate it to the same code.

     

    Sometimes if there is a new type of device emitting new types of sourceTrigger events and I haven't updated EventRunner to deal with that using the <deviceId>:<property> format, one can still use the full sourceTrigger as in the example above.

     

    However, the real power of EventRunner comes with the fact that we can post our own events. As long as it's a table with a type field it's considered an event by EventRunner.

    In a previous post I show an example on how to catch centralSceneEvents and re-post them as #Key events.

    This allows us to structure our home automation rules so that they still makes sense when we have to update them a month later :-) 

     

    The fact that we can also post events at a given time in the future allows us to make timers in a simple way.

    Please login or register to see this code.

    It's also a simple way to make loops

    Please login or register to see this code.

    We can also use the same technique to define state-machines (useful in more complex automation  systems) and specific "flows" throughout rules. I will come back on that later.

    In effect, EventScript - what I call the language - is a (Turing) complete programming language getting it's inspiration from languages like Prolog and Erlang but adapted to deal with time and timers and concepts making it suitable for describing home automation tasks...

     

     

    Edited by jgab
    • Like 1
    • Thanks 2
    Link to comment
    Share on other sites

  • Topic Author
  • On 9/14/2020 at 4:14 AM, Earybeongbeong7 said:

    Hello. I am using ER4 well. I would like to provide additional comments.

     

    I wish there was a restart button for ER4 and an update confirmation button (or automatic update on-off button, etc.) on the preview screen of ER4. It looks like a good test for home center errors (or other checks).

    I have pushed 0.5v16 that has a new model for handling UI buttons.

    You can add your own buttons in the UI for ER and handle them.

    Ex. define a button 'Label' = "Restart" with the 'ID' = "restart". The onReleased you can leave.

    Then define a rule like ´below and you have a restart button

    Please login or register to see this code.

    Insert another button to do update (if available)

    button 'Label' = "Update" with the 'ID' = "update". (this will be instead of the regular 05:00 update check)

    Please login or register to see this code.

     
    The ER4 comes with some pre-defined buttons for turning on/off some debug flags.
    However, the thinking has been that you define your own buttons in ER4 that trigger the rules you want. ...and now it's possible.
     
    Edited by jgab
    Link to comment
    Share on other sites

    Guest kallecux

    Hi @jgab, thanks for your description!

     

    You wrote on page 1 of this post the following description:

     

    -- Turn on 2 lamps  15min past sunrise on weekdays

    Rule.eval("@sunrise+00:15 & wday('mon-fri') => {myLight1,myLight2}:on")
     

    The lamp turns on at sunrise AND the day of week is a weekday!

     

    Here you wrote:

     

    88:lux & $MyDay=='work' => log('Tada')

    This will trigger when 88's lux property change OR the fibaro global MyDay changes value.

     

    I thought, that "Tada" sounds when 88:lux changes AND $MyDay is "work" - i am confused.

     

    Is this true?:

     

    88:lux > 50 & 88:breached => log("light on")

    It will be logged when 88:lux > 50 AND 88:breached? or 

    It will be logged when 88:lux > 50 OR 88:breached?

     

    Edited by kallecux
    Link to comment
    Share on other sites

  • Topic Author
  • 13 minutes ago, kallecux said:

    Hi @jgab, thanks for your description!

    Here you wrote:

    88:lux & $MyDay=='work' => log('Tada')

    This will trigger when 88's lux property change OR the fibaro global MyDay changes value.

     

    I thought, that "Tada" sounds when 88:lux changes AND $MyDay is "work" - i am confused.

     

    Yes from a high level the rule will run (logging 'Tada') when 88:lux changes AND $MyDay is "work.

    However, describing what's going on under the hood, the rule is actually triggered either when 88:lux changes value or $MayDay changes value.

    When the rule is triggered the test (left-hand side will run) and check if 88:lux is true (which it always is if it has a lux property. We don't test what the lux value should be), and then it checks if $MyDay is "work". If the test is true it will run the action part (right-hand side) and log "TaDa"

     

    13 minutes ago, kallecux said:

     

    Is this true?:

     

    88:lux > 50 & 88:breached => log("light on")

    It will be logged when 88:lux > 50 AND 88:breached? or 

    It will be logged when 88:lux > 50 OR 88:breached?

     

    AND :-) 

    Link to comment
    Share on other sites

    Guest kallecux

    Yes yes yes - Who can read has a clear advantage!

     

    You wrote the rule is actually triggered either when 88:lux changes value or $MayDay changes value.

     

    Thanks for the enlightenment! :-)

    Link to comment
    Share on other sites

    17 hours ago, jgab said:

    I have pushed 0.5v16 that has a new model for handling UI buttons.

    You can add your own buttons in the UI for ER and handle them.

    Ex. define a button 'Label' = "Restart" with the 'ID' = "restart". The onReleased you can leave.

    Then define a rule like ´below and you have a restart button

    Please login or register to see this code.

    Insert another button to do update (if available)

    button 'Label' = "Update" with the 'ID' = "update". (this will be instead of the regular 05:00 update check)

    Please login or register to see this code.

     
    The ER4 comes with some pre-defined buttons for turning on/off some debug flags.
    However, the thinking has been that you define your own buttons in ER4 that trigger the rules you want. ...and now it's possible.
     

     

    Thank you very much for reflecting my opinion.
    I made a button like this now, and I confirmed that it works well.
    How can I turn on and off random automation from a button like this?

     

    r = rule("@@00:05 => log('5min ping between 12:00 and 15:00')") 

    rule("#UI{name='r_start'} => r.enable()"); wait(03:00); r.disable()")


    If you want to start with the "incomming trigger" turned off upon restart, can you modify it as follows?

    In this case, is the state of the button synchronized with the initial restart?

     

    _debugFlags.trigger = false -- log incoming triggers

     

     

    Please login or register to see this attachment.

    Edited by Earybeongbeong7
    Link to comment
    Share on other sites

  • Topic Author
  • On 9/18/2020 at 3:33 AM, Earybeongbeong7 said:

     

    Thank you very much for reflecting my opinion.
    I made a button like this now, and I confirmed that it works well.
    How can I turn on and off random automation from a button like this?

     

    r = rule("@@00:05 => log('5min ping between 12:00 and 15:00')") 

    rule("#UI{name='r_start'} => r.enable()"); wait(03:00); r.disable()")


    Something like below. I realise that there is no way to check of a rule is enabled or not. I will add a flag to the 'r' value returned by rule() in the next version so it's easier to toggle the enabled state. Now we have to keep the state in a variable (r_start).

    Please login or register to see this code.

    You could also let it run every 5min and check of it should ping (it doesn't take much resources)

    Please login or register to see this code.

    The Util.defTriggerVar makes an eventscript variable behave like a fibaro global - it can be used to trigger rules when it changes values. Normal eventscript variables (or lua globals) don't behave like that. It only triggers rules if it changes value, so if it is set to the same value it won't trigger a rule (like fibaro globals)

     

    Quote

    If you want to start with the "incomming trigger" turned off upon restart, can you modify it as follows?

    In this case, is the state of the button synchronized with the initial restart?

    _debugFlags.trigger = false -- log incoming triggers

    Yes, but only for the 3 debug flags that are predefined. Next version I will make a more general mechanism so it is easy to add debugFlag buttons for any debug flag.

     

    Edited by jgab
    Link to comment
    Share on other sites

    19 minutes ago, jgab said:


    Something like below. I realise that there is no way to check of a rule is enabled or not. I will add a flag to the 'r' value returned by rule() in the next version so it's easier to toggle the enabled state. Now we have to keep the state in a variable (r_start).

    Please login or register to see this code.

     

     

     

     

    1. Is this save the internal value of "r_start" in reverse?

    Please login or register to see this code.

     

     

    2. Is this the right code?

    Please login or register to see this code.

     

    Isn't the following code correct?

    Please login or register to see this code.

     

     

     

    3. Can I get more explanations on what exactly this means??

    Please login or register to see this code.

     

     

     

    Link to comment
    Share on other sites

  • Topic Author
  • 1 hour ago, Earybeongbeong7 said:

     

     

    1. Is this save the internal value of "r_start" in reverse?

    Please login or register to see this code.

     

    Yes, '!' is the. logical NOT operator.  Like "X = not X"

     

    Quote

     

    2. Is this the right code?

    Please login or register to see this code.

     

    Isn't the following code correct?

    Please login or register to see this code.

     

    Well, it just defines the rule and sets it to disabled immediately. 

    Same as 

    Please login or register to see this code.

    The idea was that it starts disabled and you enable it with the button.

     

    Quote

     

    3. Can I get more explanations on what exactly this means??

    Please login or register to see this code.

     

     

    The eventscript variable 'r_start' is defined as a trigger variable. (Uti.defTriggerVar) This means that when it changes value it can be used to trigger rules.

    When we change the value of 'r_start' in the #UI rule by doing "r_start = !r_start" it will trigger these rules.

    When set to true to will trigger the first rule and when set to false it will trigger the second (!r_start => ....)

    It then enables or disables the rule (r) and updates the text of the button (with ID 'r_start'). QA is a variable bound to the QuickApp's 'self' variable so we can use it to call methods like updateView.

     

    I made a mistake and used """ for the strings in the QA:updateView - it should be single quotes.

    Please login or register to see this code.

     

    Edited by jgab
    Link to comment
    Share on other sites

    54 minutes ago, jgab said:

     

    Yes, '!' is the. logical NOT operator.  Like "X = not X"

     

    Well, it just defines the rule and sets it to disabled immediately. 

    Same as 

    Please login or register to see this code.

    The idea was that it starts disabled and you enable it with the button.

     

     

    The eventscript variable 'r_start' is defined as a trigger variable. (Uti.defTriggerVar) This means that when it changes value it can be used to trigger rules.

    When we change the value of 'r_start' in the #UI rule by doing "r_start = !r_start" it will trigger these rules.

    When set to true to will trigger the first rule and when set to false it will trigger the second (!r_start => ....)

    It then enables or disables the rule (r) and updates the text of the button (with ID 'r_start'). QA is a variable bound to the QuickApp's 'self' variable so we can use it to call methods like updateView.

     

    I made a mistake and used """ for the strings in the QA:updateView - it should be single quotes.

    Please login or register to see this code.

     

     

    I don't understand logically about "!", so I ask again.

    Is "r_start =! r_start" correct to store "r_start = false" as "r_start = true"?
    Of course the opposite is stored as "r_start = false"?

     

    Does "r_start => r.enable ()" mean to execute the command if "r_start" is "true"?

    In other words, is it the same as "r_start==true => r.enable()"?

    In the same way, "!r_start => r.disable()" means to execute the command if "r_start" is "false"?

    That is, is it the same as "r_start == false => r.disable()"?

     

    Sorry for not understanding it properly.

     

    If you teach me, I will try my best to understand.

    Link to comment
    Share on other sites

    Jan,

    This error I have

    Please login or register to see this image.

    /monthly_2020_09/image.png.1b4cb63b9fc55646411c5f768f617226.png" />

     

    but the rule is:

    Rule.eval([[sunrise-02:30..10:30 & hal.Kamerboven:isOn & overloop.Pir:breached  =>
                    hal.Danalock:unlock; 
            log('TRIGGER:%s',tjson(env.event)); log('Kamerboven bezet pir overloop beweging Danalock  - geopend')]])
    Please advice,
    //Sjakie
     
    Link to comment
    Share on other sites

  • Topic Author
  • Just now, Earybeongbeong7 said:

     

    I don't understand logically about "!", so I ask again.

    Is "r_start =! r_start" correct to store "r_start = false" as "r_start = true"?
    Of course the opposite is stored as "r_start = false"?

    !true == false

    !false == true

    ...so yes, it sets 'r_start' to the opposite value.

     

    Just now, Earybeongbeong7 said:

    Does "r_start => r.enable ()" mean to execute the command if "r_start" is "true"?

    Yes, the rule is triggered every time 'r_start' changes value. The test, "'r_start' => ..." is true (because r_start is set to true and the rest of the rule will be run)

    Just now, Earybeongbeong7 said:

    In other words, is it the same as "r_start==true => r.enable()"?

    Yes

    Just now, Earybeongbeong7 said:

    In the same way, "!r_start => r.disable()" means to execute the command if "r_start" is "false"?

    That is, is it the same as "r_start == false => r.disable()"?

    Yes. 

     

    Just now, Earybeongbeong7 said:

    Sorry for not understanding it properly.

    If you teach me, I will try my best to understand.

    No problem, you are a fast learner :-)

    Link to comment
    Share on other sites

  • Topic Author
  • 2 minutes ago, Sjakie said:

    Jan,

    This error I have

    Please login or register to see this link.

     

    but the rule is:

    Rule.eval([[sunrise-02:30..10:30 & hal.Kamerboven:isOn & overloop.Pir:breached  =>
                    hal.Danalock:unlock; 
            log('TRIGGER:%s',tjson(env.event)); log('Kamerboven bezet pir overloop beweging Danalock  - geopend')]])
    Please advice,
    //Sjakie
     

     

    So I don't have any locks. The code device:unlock results in a fibaro.call(device,"unsecure") that have worked for other users in the past. However, it doesn't seem to be the right action for your lock to unlock....  (have you tried "hal.Danalock:value=1" or "hal.Danalock:value=0" ?)

    Can you send me the device definition struct for your lock?

    Please login or register to see this code.

    or if you copy from the swagger UI.

    Link to comment
    Share on other sites

    On 9/18/2020 at 3:29 PM, jgab said:


    Something like below. I realise that there is no way to check of a rule is enabled or not. I will add a flag to the 'r' value returned by rule() in the next version so it's easier to toggle the enabled state. Now we have to keep the state in a variable (r_start).

    Please login or register to see this code.

    You could also let it run every 5min and check of it should ping (it doesn't take much resources)

    Please login or register to see this code.

    The Util.defTriggerVar makes an eventscript variable behave like a fibaro global - it can be used to trigger rules when it changes values. Normal eventscript variables (or lua globals) don't behave like that. It only triggers rules if it changes value, so if it is set to the same value it won't trigger a rule (like fibaro globals)

     

    Yes, but only for the 3 debug flags that are predefined. Next version I will make a more general mechanism so it is easy to add debugFlag buttons for any debug flag.

     

     

    Thank you for your kind explanation.

     

    I understood the code below.

     

    Please login or register to see this code.

     

    But I didn't understand the code below.  Can I get help?

     

    Please login or register to see this code.

     

    There is no “r.enable()” with the above code alone.  But I don't know how to run the r rule.

     

    Also, can you see "r" as any character?

     

    In other words, is it possible with characters other than “r” (ex. a, b, abc ...)? (a.enable(), b.enable(), abc.enable() ..)

     

    Link to comment
    Share on other sites

  • Topic Author
  •  

    Quote

    But I didn't understand the code below.  Can I get help?

     

    Please login or register to see this code.

     

     

    The @@00:05 will trigger the rule every 5min, 24x7.

    However, the action (log) will only run if the test 12:00..15:00 is true,

    We add another condition that the variable 'r_start' also needs to be true. (something we change with the button)

     

    It's not so very wasteful to trigger the rule every 5min, even if the test is not true, It takes just a few ms every 5min to make the test.

     

    Quote

    There is no “r.enable()” with the above code alone.  But I don't know how to run the r rule.

     

    Yes, so we don't enable/disable the rule. It triggers every 5min but the action only runs if "r_start & 12:00..15:00 " is true.

     

    Quote

     

    Also, can you see "r" as any character?

     

    Yes, of course,

     

    Quote

     

    In other words, is it possible with characters other than “r” (ex. a, b, abc ...)? (a.enable(), b.enable(), abc.enable() ..)

     

    Yes you can use any valid Lua variable.

    Edited by jgab
    Link to comment
    Share on other sites

    2 hours ago, jgab said:

     

    But I didn't understand the code below.  Can I get help?

     

    Please login or register to see this code.

    The @@00:05 will trigger the rule every 5min, 24x7.

    However, the action will only if the test 12:00..15:00 is true,

    We add another condition that the variable 'r_start' also needs to be true. (something we change with the button)

     

    It's to very wasteful to trigger the rule every 5min, event if the test is not true, It takes just a few ms every 5min to make the test.

     


    do the two codes below mean the same thing?

    Please login or register to see this code.

    Please login or register to see this code.

     

    And

    Please login or register to see this code.

    In the above code, “r_start =>” is a condition that triggers when “r_start is true”. What does it mean to have “r_start = true” at the end?

     

    In other words, is the code below an efficient code that consumes a little less of the resource (or load) of “HC3”?

     

    8 hours ago, Earybeongbeong7 said:

    Util.defTriggerVar("r_start",false) -- start disabled

    rule("#UI{name='r_start'} => r_start = !r_start")

    r = rule("@@00:05 & 12:00..15:00 => log('5min ping between 12:00 and 15:00')").disable() -- start disabled

    rule("r_start => r.enable(); QA:updateView('r_start','text','Rule ON')")

    rule("!r_start => r.disable(); QA:updateView('r_start','text','Rule OFF')")

     

    On the contrary, can you understand that the same effect as enable() by button operation is under the conditions below, which is an automation without a condition that triggers every 5 minutes?

     

    Please login or register to see this code.

     

    Edited by Earybeongbeong7
    Link to comment
    Share on other sites

  • Topic Author
  • My last answer was not so good - a crucial "not" was missing in one of the sentences (not easy to type on the iPhone).

     

    We have 2 variants of the rule.

    1. Disabling the @@-loop rule when we press a button. We use r.disable() and r.enable(). Note that if we enable the rule at 10:00 it will only ping between 12-15 because we have the test "12:00..15:00".

    2. Adding a extra check in the @@-loop that the variable 'r_start' must also be true if it should ping. This rule will trigger every 5min 24x7, but only ping when 'r_start==true' and "12:00..15:00"

     

    Rule 1 is a little more efficient as it disables the rule. In practice it will not show a difference on the HC3 load. It only runs every 5min to and the check takes milliseconds. You could have a 100 of these loops going on every 5min.

    If you have a rule that runs every second (@@00:00:01 => ...) I would recommend to disable it when it doesn't need to run.

     

     

    7 hours ago, Earybeongbeong7 said:


    do the two codes below mean the same thing?

    Please login or register to see this code.

    Please login or register to see this code.

    Yes the two rules. above are equal. 

     

    Quote

    And

    Please login or register to see this code.

    In the above code, “r_start =>” is a condition that triggers when “r_start is true”. What does it mean to have “r_start = true” at the end?

     

    The "r_start = true" and the "r_start = false" are unnecessary. The rule ""r_start => r_start = true; ..." trigger because 'r_start' is true so unnecessary to set it again. Sorry for the confusion. (changed it in the original post)

     

     

    Quote

     

    In other words, is the code below an efficient code that consumes a little less of the resource (or load) of “HC3”?

     

    The variant that disables the rule is a little more efficient (less load. on the HC3) but it's so small you will not notice in this case.

     

    Quote

    On the contrary, can you understand that the same effect as enable() by button operation is under the conditions below, which is an automation without a condition that triggers every 5 minutes?

    Please login or register to see this code.

     

    missing @@00:05 in the above rule.

     

    Please login or register to see this code.

     

    Edited by jgab
    Link to comment
    Share on other sites

    1 hour ago, jgab said:

    My last answer was not so good - a crucial "not" was missing in one of the sentences (not easy to type on the iPhone).

     

    We have 2 variants of the rule.

    1. Disabling the @@-loop rule when we press a button. We use r.disable() and r.enable(). Note that if we enable the rule at 10:00 it will only ping between 12-15 because we have the test "12:00..15:00".

    2. Adding a extra check in the @@-loop that the variable 'r_start' must also be true if it should ping. This rule will trigger every 5min 24x7, but only ping when 'r_start==true' and "12:00..15:00"

     

    Rule 1 is a little more efficient as it disables the rule. In practice it will not show a difference on the HC3 load. It only runs every 5min to and the check takes milliseconds. You could have a 100 of these loops going on every 5min.

    If you have a rule that runs every second (@@00:00:01 => ...) I would recommend to disable it when it doesn't need to run.

     

     

    Yes the two rules. above are equal. 

     

     

    The "r_start = true" and the "r_start = false" are unnecessary. The rule ""r_start => r_start = true; ..." trigger because 'r_start' is true so unnecessary to set it again. Sorry for the confusion. (changed it in the original post)

     

     

     

    The variant that disables the rule is a little more efficient (less load. on the HC3) but it's so small you will not notice in this case.

     

    missing @@00:05 in the above rule.

     

    Please login or register to see this code.

     

     

     

    Now I understand.  Thank you very much.

    Link to comment
    Share on other sites

    Util.defTriggerVar("test",false)

     

    In this command, we can see that the local variable (test) acts as a trigger.
    Does this local variable only have true and false values?
    If you execute a command like the one below, does it have an arbitrary value?
    If so, the command below cannot be executed, right?

     

    Util.defTriggerVar("test",home)

    rule("test=> lamp: on")

    rule("!test=> lamp: off")

    Link to comment
    Share on other sites

  • Topic Author
  • 4 hours ago, Earybeongbeong7 said:

    Util.defTriggerVar("test",false)

     

    In this command, we can see that the local variable (test) acts as a trigger.
    Does this local variable only have true and false values?
    If you execute a command like the one below, does it have an arbitrary value?
    If so, the command below cannot be executed, right?

     

    Util.defTriggerVar("test",home)

    rule("test=> lamp: on")

    rule("!test=> lamp: off")

     

    Any value. This works:

    Please login or register to see this code.

     

    Edited by jgab
    Link to comment
    Share on other sites

    Join the conversation

    You can post now and register later. If you have an account, sign in now to post with your account.

    Guest
    Reply to this topic...

    ×   Pasted as rich text.   Paste as plain text instead

      Only 75 emoji are allowed.

    ×   Your link has been automatically embedded.   Display as a link instead

    ×   Your previous content has been restored.   Clear editor

    ×   You cannot paste images directly. Upload or insert images from URL.

    ×
    ×
    • Create New...