Ceiling fan timer with choice of runtime via eDLT

Discussion in 'Pascal Logic Code Examples' started by jasonson, May 9, 2017.

  1. jasonson

    jasonson

    Joined:
    Feb 17, 2014
    Messages:
    34
    Likes Received:
    6
    Location:
    Brisbane
    I am attempting to add a switch on my eDLT beside the master bed that enables me to (if the ceiling is fan on) select an off delay for the fan. Best for those hot evenings that turn into cold nights and I don't want the fan running when its freezing.

    I created a group (Master_Fan_Timer) and created level labels at the following levels:

    000 - 'Off'
    030 - '30 Minutes'
    060 - '60 Minutes'
    120 - '2 Hours'
    180 - '3 Hours'
    255 - '6 Hours'

    Any longer than 6 hours I will let it run all night. The choice of matching levels to minutes is purely arbitrary.

    I have a Key on my eDLT set to nudge up / nudge down the group (Master_Fan_Timer) by 5.

    In my Logic I have this:

    (The variable Fan_speed is declared in global variables)

    Code:
    //FAN TIMER
    once (GetLightingLevel("Master_Fan_Timer") > 0) and (GetLightingState("Master FAN") = ON) then
    begin
    Fan_Speed := GetLightingLevel("Master_Fan_Timer");
    case Fan_Speed of
      25 : SetLightingState("Master FAN", OFF);
      5, 55 : begin
              SetLightingLevel("Master_Fan_Timer", "30 Minutes", "0s");
              PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "0:30:00", 0%);
              SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, '30 Minutes');
           end;
      35, 115 : begin
              SetLightingLevel("Master_Fan_Timer", "60 Minutes", "0s");
              PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "1:00:00", 0%);
              SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, '60 Minutes');
           end;
      65, 175 : begin
              SetLightingLevel("Master_Fan_Timer", "2 Hours", "0s");
              PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "2:00:00", 0%);
              SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, '2 Hours');
           end;
      125, 250 : begin
              SetLightingLevel("Master_Fan_Timer", "3 Hours", "0s");
              PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "3:00:00", 0%);
              SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, '3 Hours');
           end;
      185 : begin
              SetLightingLevel("Master_Fan_Timer", "6 Hours", "0s");
              PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "6:00:00", 0%);
              SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, '6 Hours');
           end;
    end;
    end;
    
    once (GetLightingState("Master FAN") = OFF) then
    begin
      SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, 'Off');
      Fan_Speed := 0;
    end;
    
    I'm yet to test this. About to upload to my screen and check it out. But I am unsure if a shorter Pulse command will 'overwrite' a longer pulse command if given after. So say i scroll right to 2 hours then back to 1 hour. Will the 1 hour pulse overwrite the group thats been told to pulse for 2 hours moments beforehand?

    Also is there a more elegant way to achieve this?
     
    Last edited by a moderator: May 9, 2017
    jasonson, May 9, 2017
    #1
  2. jasonson

    Ashley

    Joined:
    Dec 1, 2005
    Messages:
    1,524
    Likes Received:
    173
    Location:
    Adelaide, Australia
    You can only have one pulse time (or any timer) running on a single ga at a time, so anytime you issue another pulse command (or in fact any command to that ga), the previous pulse will be cancelled and the new one take over.

    Your code won't work anyhow. A ONCE command is triggered by a False to True transition of the expression, so once MASTER_FAN_TIMER is non-zero, the ONCE won't be triggered until it (or MASTER_FAN) has gone back to zero. So any other changes to the MASTER_FAN_TIMER ga will be ignored.

    I don't really understand what the case statement is trying to accomplish either.
     
    Ashley, May 9, 2017
    #2
  3. jasonson

    jasonson

    Joined:
    Feb 17, 2014
    Messages:
    34
    Likes Received:
    6
    Location:
    Brisbane
    That is what I had assumed, thank you for clarifying.

    I see my mistake, I had originally planned on using ONCE to capture any changes to MASTER_FAN_TIMER level but used state in the example above. I have fixed that to capture any changes above 0 level.

    It controls the dynamic label on the eDLT and also chooses how long to run the fan for. So if I press the DLT button once only it will first nudge MASTER_FAN_TIMER from 0 to 5, which in logic is picked up in the case statement and adjusted up to 30 and the group is pulsed for 30 minutes at current speed. If I press it again to nudge up it will nudge to 35 which is then picked in logic and adjusted up to 60 minutes and so on. If i nudge down when at 60 to 55, logic moves it back to 30 minutes and re-pulses the group to run the fan for 30 minutes. The label updates the fan timer on the eDLT so the I get feed back on which time limit I am choosing.

    The reason I chose the levels and used a 5 point nudge is so at any stage I can know the level of MASTER_FAN_TIMER and be able to move to it's predecessor or successor as needed if that makes sense.

    Similar to using a scene cycle function and having the scene name appear. This uses only 1 group and some logic.

    I will be uploading the above code to the screen in the next few minutes to test. I will report back if the code works soon.
     
    jasonson, May 9, 2017
    #3
  4. jasonson

    jasonson

    Joined:
    Feb 17, 2014
    Messages:
    34
    Likes Received:
    6
    Location:
    Brisbane
    Ashley, I re-read what you said as I was uploading and realized my changes will not matter as the ONCE is only triggered on a rising or falling edge. Value changes don't trigger in the way I am trying to monitor them.

    I might have to change it to all separate ONCE statements that each compare to a certain level in master_fan_timer.

    Code:
    //FAN TIMER
    once (GetLightingLevel("Master_Fan_Timer") = 5) or (GetLightingLevel("Master_Fan_Timer") = 55) then
      begin
      if (GetLightingState("Master FAN") = ON) then
        begin 
          Fan_Speed := GetLightingLevel("Master_Fan_Timer");
          SetLightingLevel("Master_Fan_Timer", "30 Minutes", "0s");
          PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "0:30:00", 0%);
          SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, '30 Minutes');
        end
        else
          begin
            SetLightingState("Master FAN", OFF);
        end;
        
     end;
     
    once (GetLightingLevel("Master_Fan_Timer") = 35) or (GetLightingLevel("Master_Fan_Timer") = 115) and (GetLightingState("Master FAN") = ON) then
      begin
        SetLightingLevel("Master_Fan_Timer", "60 Minutes", "0s");
        PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "1:00:00", 0%);
        SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, '60 Minutes');
    end;
    
    once (GetLightingLevel("Master_Fan_Timer") = 65) or (GetLightingLevel("Master_Fan_Timer") = 175) and (GetLightingState("Master FAN") = ON) then
      begin
        SetLightingLevel("Master_Fan_Timer", "2 Hours", "0s");
        PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "2:00:00", 0%);
        SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, '2 Hours');
    end;
    
    once (GetLightingLevel("Master_Fan_Timer") = 125) or (GetLightingLevel("Master_Fan_Timer") = 250) and (GetLightingState("Master FAN") = ON) then
      begin
        SetLightingLevel("Master_Fan_Timer", "3 Hours", "0s");
        PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "3:00:00", 0%);
        SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, '3 Hours');
    end;
    
    once (GetLightingLevel("Master_Fan_Timer") = 185) and (GetLightingState("Master FAN") = ON) then
      begin
        SetLightingLevel("Master_Fan_Timer", "6 Hours", "0s");
        PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "6:00:00", 0%);
        SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, '6 Hours');
    end;
    
    once (GetLightingState("Master FAN") = OFF) or (GetLightingLevel("Master_Fan_Timer") = 25) then
      begin
        SetLightingState("Master_Fan_Timer", OFF);
        SetStringIBSystemIO("Label Group Text", "COOLRIDGE", "Lighting", "Master_Fan_Timer", Variant1, 'Off');
        Fan_Speed := 0;
    end;
    
    THIS CODE WORKS LIKE A TREAT... ~Mostly~

    Last things I will tackle tomorrow now is that if I turn the timer off manually at the eDLT, there is still the latest Pulse command at 30 minutes controlling the group. This will need to be overwritten with a setlevel command to maintain the fan if on at the same level and look into if changing the fan speed, the timer will need addressed as it was a pulse. Maybe switching to a timer or using a variable that compares to the current time and adds x minutes then group will turn off when the time = x. Guess I just need to sleep and look again tomorrow night.

    I don't normally goto this extreme, but if i implemented this in a client installation i would want to make it bullet proof.

    Is there a more elegant way of doing this though.
     
    Last edited by a moderator: May 9, 2017
    jasonson, May 9, 2017
    #4
  5. jasonson

    NickD Moderator

    Joined:
    Nov 1, 2004
    Messages:
    1,420
    Likes Received:
    62
    Location:
    Adelaide
    You could probably do it using the scene cycle function, with empty scenes.

    Your logic just needs to look at the trigger event that comes out and set the timer based on that.

    Doing it this way
    a) you only get the one event, and
    b) you can name each scene with the time delay, then you will see the different times on the switch as you cycle through

    You could also do it the way you have, but use the multi-level widget rather than nudge, which will also allow you to label the times, but you are restricted to 4 levels (ie 4 timer durations).

    Nick
     
    NickD, May 10, 2017
    #5
  6. jasonson

    jasonson

    Joined:
    Feb 17, 2014
    Messages:
    34
    Likes Received:
    6
    Location:
    Brisbane
    But then I use 5 scenes for essentially one thing.

    Using logic to update the dynamic labels gives me exactly that.

    Wouldn't you only get 3 levels as 1 level would be OFF?

    Appreciate your input though mate. All helps with learning.
     
    Last edited by a moderator: May 12, 2017
    jasonson, May 12, 2017
    #6
  7. jasonson

    NickD Moderator

    Joined:
    Nov 1, 2004
    Messages:
    1,420
    Likes Received:
    62
    Location:
    Adelaide
    Yes... assuming you really need 5 options.

    Yes, but sending labels from logic is slow and a results in a pretty horrible user experience IMO. The scene labels are stored locally so the update on the screen is instant.

    Yes, but how many do you really need?

    Nick
     
    NickD, May 12, 2017
    #7
  8. jasonson

    jasonson

    Joined:
    Feb 17, 2014
    Messages:
    34
    Likes Received:
    6
    Location:
    Brisbane
    You make a very valid point haha. It started out with me wanting an OFF delay timer for the bedroom fan, then wanting nice labels for timer, then added multiple times then i was knee deep in a massive case statement playing with logic.

    I have now learned the HasChanged statement. This is what my original code was missing.

    But agree with you a Multilevel control is better.

    The code is MUCH simpler and the label update is nice and fast.

    Updated code:
    Code:
    //FAN TIMER
    once HasChanged(GetLightingLevel("Master_Fan_Timer")) then
      begin
      if (GetLightingState("Master FAN") = ON) then
        begin
          Fan_Speed := GetLightingLevel("Master FAN");
          case GetLightingLevel("Master_Fan_Timer") of
            0 : SetLightingLevel("Master FAN", Fan_Speed, "0s");
            85 : PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "1:00:00", 0%);
            170 : PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "3:00:00", 0%);
            255 : PulseCBusLevel("COOLRIDGE", "Lighting", "Master FAN", Fan_Speed, "0s", "6:00:00", 0%);
          end;
         end   
      else
        begin
          SetLightingState("Master_Fan_Timer", OFF);
      end;
    end;
    
     
    jasonson, May 12, 2017
    #8
  9. jasonson

    Conformist

    Joined:
    Aug 4, 2004
    Messages:
    760
    Likes Received:
    67
    Location:
    Adelaide, South Australia
    What about using a temperature sensor that will trigger logic to either switch the fan off when the temp falls below a threshold or, set your duration(s) based on temp thresholds. I use outdoor temperature to determine how long my under-tile heating runs for as well as what time my a/c come on to pre-heat the house. Kinda love fully auto option that I don't need to think about :eek:
     
    Conformist, May 12, 2017
    #9
  10. jasonson

    Wonkey

    Joined:
    Aug 3, 2004
    Messages:
    395
    Likes Received:
    37
    Location:
    Adelaide
    If you are going to use the Multi level control then why not use these levels to remote trigger a scene in your PAC, C-touch or Wiser and simply pulse the GA in the scene for the required period. A lot simpler and more reliable.
    Colin
     
    Wonkey, May 13, 2017
    #10
  11. jasonson

    jasonson

    Joined:
    Feb 17, 2014
    Messages:
    34
    Likes Received:
    6
    Location:
    Brisbane
    Serious question here. Is there a reason that using scenes to pulse a GA would be more reliable than using logic to do the exact same thing?

    The other thing scenes do not capture is if the FAN is running or not. If I use scenes, the user has no option over the FAN speed only what level I set in the scene.

    Using logic, the fan timer does nothing unless the FAN is already on. Once the user selects a speed they can then use the next button down to select an off delay if needed.

    The only issue is if they change the FAN speed, the timer will discontinue when using the PULSE command as the GA LEVEL has been updated overwriting the PULSE command.

    I might go back and revisit one of my ideas of using a variable to store the time and add 'X' time to it, then when the time is equal to the time in the variable then the FAN will turn off. That way it allows the fan speed to be changed by the user independently of the timer control.

    I know alot of people reading this and even contributing will not see the point, but this is how I best learn. I like to experiment and use, even misuse, things to better understand the concepts, uses, limitations and potential problems of doing certain things certain ways.

    I do appreciate every comment in this thread as each has helped me see a different approach and asses each on merit and function in my situation.
     
    jasonson, May 13, 2017
    #11
  12. jasonson

    NickD Moderator

    Joined:
    Nov 1, 2004
    Messages:
    1,420
    Likes Received:
    62
    Location:
    Adelaide
    I suppose the logic might be considered slightly less reliable because if something else goes wrong in your logic and causes the logic engine to restart (eg not checking a socket is valid before using it), then you would lose your running timers etc.

    Other than that, there's no reason I can think of that logic should be considered less reliable than scenes.

    Nick
     
    NickD, May 15, 2017
    #12
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.