more G-Labs products

Author Topic: SmartControl  (Read 5917 times)

May 28, 2014, 10:40:36 AM
Read 5917 times

Boeky

  • **
  • Information
  • Jr. Member
  • Posts: 31
Hi,


Now that I've managed to get the MCP23017 working correctly, I started writing a SmartControl module to use the inputs of the MCP23017 as control buttons :

1 button mode :
short press (<2seconds) -> Toggle On and Off
long press (>2seconds) -> change the level by +5 or -5, changing bright and dim every time i release the button
(sensors are working inversed for now : so they are at 1 all the time and a key press sets them to 0)

The program I made so far work without errors but when it is entering the long press loop it changes the module level by 5 every 10 seconds or more instead of the pause of 1 second.
Am I doing it right or do i loch up some other triggers by programming this way?

Code: [Select]
var SMART_CONTROL_ENABLE = "HomeGenie.SmartControl.Enable";
var SENSOR_NAME = "HomeGenie.SmartControl.PushSensor";

var smart_devices = Modules.WithFeature(SMART_CONTROL_ENABLE);
//
When.ModuleParameterIsChanging((module, parameter) => {
   
  if (module.IsOfDeviceType("Sensor"))
    {
      if (parameter.Name == "Status.Level")
        {
            var motionlevel = parameter.DecimalValue;
            if (motionlevel < 1)
            {
                smart_devices.Each((mod)=>
                {
                    if (mod.Parameter(SENSOR_NAME).Value == module.Instance.Name)
                    {
                     
                      Pause(1);
                      if (module.Level == 0)
                      {
                        while(module.Level == 0)
                        {
                           mod.Level = (mod.Level - 5);
                           // If i set a pause of 1 second it somhow takes 10 seconds
                           //Pause(1);
                        }
                     
                       
                      }
                      else {
                         mod.Toggle();
                      }
  //if (mod.Level == 0)
                        //{
                        //Program.Notify("Smart Control", module.Instance.Name + "<br> Toggled " + mod.Instance.Address + " " + mod.Instance.Name);
                         
                        //}
                     
                     
                       
                    }
                  //
              return false;
                });
            }
            else
            {
                // timeout before turning it off
            }
        }
    }
    return true;
});
Program.GoBackground();

Thanks,
Christophe
« Last Edit: May 28, 2014, 11:59:19 AM by Boeky »

May 28, 2014, 01:02:01 PM
Reply #1

Gene

  • *****
  • Information
  • Administrator
  • Posts: 1472
  • Tangible is the future!
    • Yet Another Programmer
When the button is released (Status.Level == 1 in your case) you can measure the press duration by simply diffing these two dates:

Code: [Select]

var endTime = parameter.UpdateTime; // this is the timestamp of button release
var startTime = parameter.LastUpdateTime; // this is the timestamp of button press
var pressDuration = endTime - startTime;

if (pressDuration.TotalSeconds > 2)
{
   // is a long press
}
else
{
   // is a short press
}


So you don't need to use a loop. In general it's best to avoid long time tasks inside the event handler.
Hope this helps.

g.

May 28, 2014, 04:01:59 PM
Reply #2

Boeky

  • **
  • Information
  • Jr. Member
  • Posts: 31
Hi,


I tried implementing the code but nothing works. Not even a switch on or off.

It also seems that your code invokes only once after the button is released,


What I need is something that works like this :
Short press : Toggle command
Long press : dimming or brightning by 5 command in a loop of each second  as long as i hold the key. After releasing the loop should stop.

The problem is that the code works fine but it is executing the commands of the loop only once every 10 - 20 seconds instead of 1 second...

I already tried running an asynctask but the problem remains...


Thanks,
Christophe


May 28, 2014, 06:45:54 PM
Reply #3

Gene

  • *****
  • Information
  • Administrator
  • Posts: 1472
  • Tangible is the future!
    • Yet Another Programmer
Try this:

Code: [Select]

var SMART_CONTROL_ENABLE = "HomeGenie.SmartControl.Enable";
var SENSOR_NAME = "HomeGenie.SmartControl.PushSensor";

int LEVEL_ON = 0;
int LONG_PRESS_SECONDS = 2;

var smart_devices = Modules.WithFeature(SMART_CONTROL_ENABLE);
//
When.ModuleParameterIsChanging((module, parameter) =>
{
    // TODO: the sensor buttons should also be marked with a specific feature
    if (module.IsOfDeviceType("Sensor"))
    {
        if (parameter.Name == "Status.Level")
        {
            var sensorLevel = parameter.DecimalValue;
            // store the initial timestamp
            var levelTimestamp = parameter.UpdateTime.Ticks;
            if (sensorLevel == LEVEL_ON)
            {
                Program.RunAsyncTask(() =>
                {

                    // wait for the long press delay
                    Pause(LONG_PRESS_SECONDS);
                    // while the button is kept pressed...
                    while (levelTimestamp == parameter.UpdateTime.Ticks)
                    {
                        // Dim all SmartControl enabled dimmers by 5%
                        smart_devices.Each((mod) =>
                        {
                            if (mod.Parameter(SENSOR_NAME).Value == module.Instance.Name)
                            {
                                mod.Level -= 5;
                            }
                            return false;
                        });
                    }

                });
            }
            else
            {
                // timeout before turning it off
                var endTime = parameter.UpdateTime;
                var startTime = parameter.LastUpdateTime;
                var pressDuration = new TimeSpan(endTime.Ticks - startTime.Ticks);
                // short press
                if (pressDuration.TotalSeconds < LONG_PRESS_SECONDS)
                {
                    smart_devices.Each((mod) =>
                    {
                        if (mod.Parameter(SENSOR_NAME).Value == module.Instance.Name)
                        {
                            mod.Toggle();
                        }
                        return false;
                    });
                }
            }
        }
    }
    return true;
});
Program.GoBackground();



I cannot test it right now because my only develop raspi is into the Touch Panel.
But this is the way the code should work.

Cheers,
g.



« Last Edit: May 28, 2014, 07:06:37 PM by Gene »

May 30, 2014, 03:28:12 PM
Reply #4

Gene

  • *****
  • Information
  • Administrator
  • Posts: 1472
  • Tangible is the future!
    • Yet Another Programmer
Hi Christophe,

did you succeed in getting this to work?

Cheers,
g.

May 31, 2014, 02:18:00 AM
Reply #5

Boeky

  • **
  • Information
  • Jr. Member
  • Posts: 31
Hi Gene,

I didn't had the time to try your code for now.
I'll let you know if it works as soon as I can continu this week.

Thanks for the great support.
« Last Edit: May 31, 2014, 02:25:35 AM by Boeky »

June 02, 2014, 01:42:54 PM
Reply #6

Boeky

  • **
  • Information
  • Jr. Member
  • Posts: 31
Hi gene,

I tested your code but it gives exact the same problem as my code :

On and Off works great but when I dim (holding button down for more then 2 seconds) it starts dimming by 5 but it lowers the value with 5 every 10 or more seconds... The pauses between lowering are getting longer and longer each time it lowers by 5. Actually there is not any fixed time between them The time is random it is just getting longer and longer as long as you keep holding the button...

The program itself works but I dont know why it takes more then 10 seconds ...


Overview Logfile

14:00:37.896 Status.Level 0 5 Components.MCP23017 (RELEASE BUTTON)
14:00:35.845 Status.Level 0.74 B1 HomeAutomation.X10
14:00:25.785 Status.Level 0.78 B1 HomeAutomation.X10
14:00:25.785 Status.Level 0.78 B1 HomeAutomation.X10
14:00:15.845 Status.Level 0.82 B1 HomeAutomation.X10
14:00:05.819 Status.Level 0.86 B1 HomeAutomation.X10
13:59:55.750 Status.Level 0.9 B1 HomeAutomation.X10
13:59:55.750 Status.Level 0.9 B1 HomeAutomation.X10
13:59:47.725 Status.Level 0.95 B1 HomeAutomation.X10
13:59:45.698 Status.Level 1 5 Components.MCP23017 (PRESS AND HOLD BUTTON)
13:59:43.17 Status.Level 1 B1 HomeAutomation.X10 (MODULE ON TO START DIMMING)
13:59:42.979 Status.Level 0 5 Components.MCP23017
13:59:42.707 Status.Level 1 5 Components.MCP23017 (SHORT PRESS BUTTON)
13:59:38.113 Status.Level 0 B1 HomeAutomation.X10 (MODULE OFF)
13:59:38.100 Status.Level 0 5 Components.MCP23017
13:59:37.874 Status.Level 1 5 Components.MCP23017 (SHORT PRESS BUTTON)
13:59:33.921 Status.Level 1 B1 HomeAutomation.X10 (MODULE ON)
13:59:33.905 Status.Level 0 5 Components.MCP23017
13:59:33.677 Status.Level 1 5 Components.MCP23017 (SHORT PRESS BUTTON)



Thanks,
Christophe
« Last Edit: June 02, 2014, 02:04:16 PM by Boeky »

June 02, 2014, 02:40:29 PM
Reply #7

Gene

  • *****
  • Information
  • Administrator
  • Posts: 1472
  • Tangible is the future!
    • Yet Another Programmer
Let's do some tests.

Change this piece of code

Code: [Select]
                    // while the button is kept pressed...
                    while (levelTimestamp == parameter.UpdateTime.Ticks)
                    {
                        // Dim all SmartControl enabled dimmers by 5%
                        smart_devices.Each((mod) =>
                        {
                            if (mod.Parameter(SENSOR_NAME).Value == module.Instance.Name)
                            {
                                mod.Level -= 5;
                            }
                            return false;
                        });
                    }

to this:

Code: [Select]
                    // while the button is kept pressed...
                    while (levelTimestamp == parameter.UpdateTime.Ticks)
                    {
                        // Dim all SmartControl enabled dimmers by 5%
                        Program.Notify("SmartControl", "Dimming by 5%");
                    }

Then try it and see the log.
This is just to understand where is the cause of the excessive delay.

Probably the Each or the mod.Parameter(SENSOR_NAME) is taking lot of time.
So we can either optimize these functions, or selecting the modules before entering the loop, so that no other time-consuming operations are made inside the loop.

Cheers,
g.
« Last Edit: June 02, 2014, 02:43:29 PM by Gene »

June 02, 2014, 03:29:28 PM
Reply #8

Boeky

  • **
  • Information
  • Jr. Member
  • Posts: 31
Hi Gene,


The first attempt of your code resulted in a script lock because there was no pause and the Notify was executed too much so Internet Explorer forced te script to stop and reloaded the webpage.

So I added a Pause of 1 second and this looks better :

Code: [Select]
14:58:06.966 Status.Level 0 5 Components.MCP23017 (WHY 3 TIMES ?)
14:58:06.966 Status.Level 0 5 Components.MCP23017
14:58:06.966 Status.Level 0 5 Components.MCP23017 (BUTTON RELEASE)
14:58:06.606 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:58:05.605 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:58:04.604 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:58:03.603 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:58:02.603 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:58:01.602 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:58:00.601 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:59.600 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:58.600 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:57.599 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:56.598 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:55.598 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:54.597 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:53.596 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:52.595 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:51.595 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:50.594 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:49.559 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:48.559 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:48.559 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:47.558 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:46.557 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:45.556 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:44.556 SmartControl Dimming by 5% 1001 HomeAutomation.HomeGenie.Automation
14:57:42.543 Status.Level 1 5 Components.MCP23017 (START PRESS AND HOLD)
14:57:38.999 Status.Level 1 B1 HomeAutomation.X10 (MODULE ON TO START DIMMING)
14:57:38.986 Status.Level 0 5 Components.MCP23017
14:57:38.761 Status.Level 1 5 Components.MCP23017 (SHORT PRESS)
14:57:36.998 Status.Level 0 B1 HomeAutomation.X10 (MODULE OFF)
14:57:36.981 Status.Level 0 5 Components.MCP23017
14:57:36.761 Status.Level 1 5 Components.MCP23017 (SHORT PRESS)
14:57:32.911 Status.Level 1 B1 HomeAutomation.X10 (MODULE ON)
14:57:32.894 Status.Level 0 5 Components.MCP23017
14:57:32.663 Status.Level 1 5 Components.MCP23017 (SHORT PRESS)

SO I'm trying to declare a targetModule out of the RunAsyncTask Scope now but it is still inside the 'smart_devices.Each((mod) =>' scope so I cannot acces is from the AsyncTask Scope. How do I declare the variable outside the Scope so I can acces it within the asynctask Scope?


Thanks
« Last Edit: June 02, 2014, 08:25:12 PM by Boeky »

June 02, 2014, 04:28:10 PM
Reply #9

Gene

  • *****
  • Information
  • Administrator
  • Posts: 1472
  • Tangible is the future!
    • Yet Another Programmer
Wait a minute =)

Since the cause seems to be the "Each" function I suggest the following:

Code: [Select]
                    // After the 2 seconds delay let's buid the modules list
                    if (levelTimestamp == parameter.UpdateTime.Ticks)
                    {
                           var smartModules = new List<ModuleHelper>();
                           smart_devices.Each((mod) =>
                           {
                                if (mod.Parameter(SENSOR_NAME).Value == module.Instance.Name)
                                {
                                    smartModules.Add(mod);
                                }
                                return false;
                           });

                            // while the button is kept pressed...
                            while (levelTimestamp == parameter.UpdateTime.Ticks)
                            {
                                // Dim all SmartControl enabled dimmers by 5%
                                foreach(var mod in smartModules)
                                {
                                       mod.Level -= 5;
                                }
                            }
                    }

That way the Each function will be called just once. You don't need to use RunAsyncTask.
The Each function seem to be slow on raspberry since it's not just iterating modules, but also filtering agaist all the filters applied to the selection.
So it's best to call it just once, before doing the dim job.

Cheers,
g.

June 02, 2014, 07:26:12 PM
Reply #10

bkenobi

  • *****
  • Information
  • Global Moderator
  • Posts: 1525
Gene,

Do you think this could be part of why the X10 speed is slow for me?  I'm using the Each command there as well.  I know you optimized X10 quite a bit, but it seemed like there was a little variation that could be either very quick or a little bit slow (not 10 seconds any more, but 1-2 maybe).

June 02, 2014, 07:57:41 PM
Reply #11

Gene

  • *****
  • Information
  • Administrator
  • Posts: 1472
  • Tangible is the future!
    • Yet Another Programmer
Hi Ben,

yes it could.

June 02, 2014, 08:12:10 PM
Reply #12

bkenobi

  • *****
  • Information
  • Global Moderator
  • Posts: 1525
Interesting.  I'll have to look at moving the code out of the Each loops then.

June 02, 2014, 08:24:04 PM
Reply #13

Boeky

  • **
  • Information
  • Jr. Member
  • Posts: 31
But again, the problem remains...


LOGBOOK
Code: [Select]
20:17:14.742 Status.Level 0 5 Components.MCP23017 (RELEASE BUTTON)
20:17:13.162 Status.Level 0.78 B1 HomeAutomation.X10
20:17:03.116 Status.Level 0.82 B1 HomeAutomation.X10 
20:16:53.100 Status.Level 0.86 B1 HomeAutomation.X10
20:16:43.83 Status.Level 0.9 B1 HomeAutomation.X10
20:16:38.775 Status.Level 0.95 B1 HomeAutomation.X10
20:16:36.751 Status.Level 1 5 Components.MCP23017 (PRESS EN HOLD)
20:16:34.457 Status.Level 1 B1 HomeAutomation.X10 (MODULE ON)
20:16:34.435 Status.Level 0 5 Components.MCP23017
20:16:34.209 Status.Level 1 5 Components.MCP23017 (SHORT PRESS)

SOURCE CODE
Code: [Select]
var SMART_CONTROL_ENABLE = "HomeGenie.SmartControl.Enable";
var DIGITAL_INPUT_MODULE_NAME = "HomeGenie.SmartControl.DigitalInputModule";
//var DIGITAL_OUTPUT_MODULE_NAME = "HomeGenie.SmartControl.DigitalOutputModule";

int LEVEL_ON = 1;
int LONG_PRESS_SECONDS = 2;


//
var smart_devices = Modules.WithFeature(SMART_CONTROL_ENABLE);
//
When.ModuleParameterIsChanging((module, parameter) => {
   
    // Adding a confirmation LED for Niko buttons with LED
    // The feature works but is currently disabled for testing the press an hold function
 
  //if (module.HasFeature(SMART_CONTROL_ENABLE) && module.Parameter(DIGITAL_OUTPUT_MODULE_NAME).Value != "" && parameter.Name == "Status.Level")
    //{
      //if(parameter.DecimalValue > 0)
      //{
       // Modules.WithName(module.Parameter(DIGITAL_OUTPUT_MODULE_NAME).Value).On();
      //}
      //else
      //{
      //  Modules.WithName(module.Parameter(DIGITAL_OUTPUT_MODULE_NAME).Value).Off();
     // }
     
    //}
 
 
 
  // PRESS EN HOLD FUNCTION
 
    // TODO: the sensor buttons should also be marked with a specific feature
    if (module.IsOfDeviceType("Sensor"))
    {
        if (parameter.Name == "Status.Level")
        {
            var sensorLevel = parameter.DecimalValue;
            var levelTimestamp = parameter.UpdateTime.Ticks;
         
            if (sensorLevel == LEVEL_ON)
            {
              Pause(LONG_PRESS_SECONDS);
              if (levelTimestamp == parameter.UpdateTime.Ticks)
                    {
                           var smartModules = new List<ModuleHelper>();
                           smart_devices.Each((mod) =>
                           {
                                if (mod.Parameter(DIGITAL_INPUT_MODULE_NAME).Value == module.Instance.Name)
                                {
                                    smartModules.Add(mod);
                                }
                                return false;
                           });

                            // while the button is kept pressed...
                            while (levelTimestamp == parameter.UpdateTime.Ticks)
                            {
                                // Dim all SmartControl enabled dimmers by 5%
                                foreach(var mod in smartModules)
                                {
                                       mod.Level -= 5;
                                }
                            }
                    }
            }
            else
            {
             
              // SHORT PRESS FUNCTION -> Works
             
                // timeout before turning it off
                var endTime = parameter.UpdateTime;
                var startTime = parameter.LastUpdateTime;
                var pressDuration = new TimeSpan(endTime.Ticks - startTime.Ticks);
                // short press
                if (pressDuration.TotalSeconds < LONG_PRESS_SECONDS)
                {
                    smart_devices.Each((mod) =>
                    {
                        if (mod.Parameter(DIGITAL_INPUT_MODULE_NAME).Value == module.Instance.Name)
                        {
                            mod.Toggle();
                        }
                        return false;
                    });
                }
            }
        }
    }
    return true;
 
     
});
Program.GoBackground();

June 02, 2014, 08:28:48 PM
Reply #14

Gene

  • *****
  • Information
  • Administrator
  • Posts: 1472
  • Tangible is the future!
    • Yet Another Programmer
MMmm.... so that was not the Each function! =/

Can you try a new script with just these instructions:

Code: [Select]

var dimmer = Modules.WithAddress("B1").Get();

dimmer.On();
Pause(1);
dimmer.Level -= 5;
dimmer.Level -= 5;
dimmer.Level -= 5;
dimmer.Level -= 5;
dimmer.Level -= 5;
dimmer.Level -= 5;
dimmer.Level -= 5;
Pause(1);
dimmer.Off();


Cheers,
g.