Connecting sensors to Mozilla’s IoT Gateway

Here is a 1st post about Mozilla’s IoT effort, and specifically the gateway project which is illustrating “Web Of Things” concept to create a decentralized Internet of Things, using Web technologies.

Today we will focus on the gateway, as it is the core component of the whole framework. Version 0.4.0 was just released, so you can try it your own on Raspberry Pi 3.  The Raspberry Pi 3 is the reference platform, but it should be possible to port to other single board computers (like ARTIK, etc).

The post will explain how to get started and how to establish basic automation using I2C sensors and actuators on gateway’s device (without any cloud connectivity).

To get started, first install the gateway according to these straightforward instructions:

Prepare SD Card

You need to download the Raspbian based gateway-0.4.0.img.zip (1GB archive) and dump it to SD card (2.6GB min).

lsblk # Identify your sdcard adapter ie:
disk=/dev/disk/by-id/usb-Generic-TODO
url=https://github.com/mozilla-iot/gateway/releases/download/0.4.0/gateway-0.4.0.img.zip
wget -O- $url | funzip | sudo dd of=$disk bs=8M oflag=dsync

If you only want to use the gateway and not hack on it, you can skip this next part which enables a developer shell though SSH.  However, if you do want access to a developer shell, mount the 1st partition called “boot” (you may need to replug your SD card adapter) and add a file to enable SSH:

sudo touch /media/$USER/boot/ssh
sudo umount /media/$USER/*

First boot

Next, install the SD card in your Raspberry PI 3 (Older RPis could work too, particularly if you have a wifi adapter).

When it has completed the first boot, you can check that the Avahi daemon is registering “gateway.local” using mDNS (multicast DNS)

ping gateway.local
ssh pi@gateway.local # Raspbian default password for pi user is "raspberry"

Let’s also track local changes to /etc by installing etckeeper, and change the default password.

sudo apt-get install etckeeper
sudo passwd pi

Logging in

You should now be able to access the web server, which is running on port 8080 (earlier version used 80):

http://gateway.local:8080/

It will redirect you to a page to configure wifi:

URL: http://gateway.local:8080/
Welcome
Connect to a WiFi network?
FreeWifi_secure
FreeWifi
OpenBar
...
(skip)

We can skip it for now:

URL: http://gateway.local:8080/connecting
WiFi setup skipped
The gateway is now being started. Navigate to gateway.local in your web browser while connected to same network as the gateway to continue setup.
Skip

After a short delay, the user should be able to reconnect to the entry page:

http://gateway.local:8080/

The gateway can be registered on mozilla.org for remote management, but we can skip this for now.

Then administrator is now welcome to register new users:

URL: http://gateway.local:8080/signup/
Mozilla IoT
Welcome
Create your first user account:
user: user
email: user@localhost
password: password
password: password
Next

And we’re ready to use it:

URL: http://gateway.local:8080/things
Mozilla IoT
No devices yet. Click + to scan for available devices.
Things
Rules
Floorplan
Settings
Log out

Filling dashboard

You can start filling your dashboard with Virtual Resources,

First hit the “burger menu” icon, go to settings page, and then go to the addons page.

Here you can enable a “Virtual Things” adapter:

URL: http://gateway.local:8080/settings/addons/
virtual-things-adapter 0.1.4
Mozilla IoT Virtual Things Adapter
by Mozilla IoT

Once enabled It should be listed along ThingURLAdapter on the adapters page:

URL: http://gateway.local:8080/settings/adapters
VirtualThingsAdapter
virtual-things
ThingURLAdapter
thing-url-adapter

You can then go back to the 1st Things page (it’s the first entry in the menu):

We can start adding “things” by pressing the bottom menu.

URL: http://gateway.local:8080/things
Virtual On/Off Color Light
Color Light
Save

Then press “Done” at bottom.

From this point, you can decide to control a virtual lamp from the UI, and even establish some basic rules (second entry in menu) with more virtual resources.

Sensing Reality

Because IoT is not about virtual worlds, let’s see how to deal with the physical world using sensors and actuators.

For sensors, there are many way to connect them to computers using analog or digital inputs on different buses.  To make it easier for applications developers, this can be abstracted using W3C’s generic sensors API.

While working on IoT.js‘s modules, I made a “generic-sensors-lite” module that abstracted a couple of I2C drivers from the NPM repository.  To verify the concept, I have made an adapter for Mozilla’s IoT Gateway (which is running Node.js), so I published the generic-sensors-lite NPM module first.

Before using the mozilla-iot-generic-sensors-adapter, you need to enable the I2C bus on the gateway (version 0.4.0, master has I2C enabled by default).

sudo raspi-config
Raspberry Pi Software Configuration Tool (raspi-config)
5 Interfacing Options Configure connections to peripherals
P5 I2C Enable/Disable automatic loading of I2C kernel module
Would you like the ARM I2C interface to be enabled?
Yes
The ARM I2C interface is enabled
ls -l /dev/i2c-1
lsmod | grep i2c
i2c_dev 16384 0
i2c_bcm2835 16384 0

Of course you’ll need at least one real sensor attached to the I2C pin of the board.  Today only 2 modules are supported:

You can double check if addresses are present on I2C the bus:

sudo apt-get install i2c-tools
/usr/sbin/i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- 23 -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77

Install mozilla-iot-generic-sensors-adapter

Until sensors adapter is officially supported by the mozilla iot gateway, you’ll need to install it on the device (and rebuild dependencies on the target) using:

url=https://github.com/rzr/mozilla-iot-generic-sensors-adapter
dir=~/.mozilla-iot/addons/generic-sensors-adapter
git clone --depth 1 -b 0.0.1 $url $dir
cd $dir
npm install

Restart gateway (or reboot)
sudo systemctl restart mozilla-iot-gateway.service
tail -F /home/pi/.mozilla-iot/log/run-app.log

Then the sensors addon can be enabled by pressing the “enable” button on the addons page:

URL: http://gateway.local:8080/settings/addons
generic-sensors-adapter 0.0.1
Generic Sensors for Mozilla IoT Gateway

It will appear on the adapters page too:

URL: https://gateway.local/settings/adapters
VirtualThingsAdapter
virtual-things
ThingURLAdapter
thing-url-adapter
GenericSensorsAdapter
generic-sensors-adapter

Now we can add those sensors as new things (Save and done buttons):

URL: http://gateway.local:8080/things
Ambient Light Sensor
Unknown device type
Save
Temperature Sensor
Unknown device type
Save

Then they will appear as:

  • http://gateway.local:8080/things/0 (for Ambient Light Sensor)
  • http://gateway.local:8080/things/1 (for Temperature Sensor)

To get value updated in the UI, they need to turned on first (try again if you find a big, and file tickets I will forward to drivers authors).

A GPIO adapter can be also used for actuators, as shown in this demo video.

If you have other sensors, check if the community has shared a JS driver, and please let me know about integrating new sensors drivers in generic-sensors-lite

How to Create an EFL Gadget Sandbox

The new gadget API and infrastructure for Enlightenment continue to undergo heavy development. In addition to improving and extending the base gadget UI, work has recently begun on creating a gadget provider with the new API to provide sandboxing and allow gadgets to be written as regular applications that don’t have or require access to compositor internals.

The primary enabler of the new sandboxing system is the efl-wl compositor widget. This allows the compositor to launch applications in isolation, and also provides the ability to add protocol extensions for only that specific instance of the compositor widget. Using these features, it becomes possible to add gadget-specific protocols and utilities on the compositor side that are passed through transparently to the client gadget application.

Currently, there is one base protocol in use: the e-gadget protocol, which looks like this:



  
    
      
      
      
    
    
      
      
      
      
      
      
    
    
      
      
      
      
      
    
    
      
    
    
      
    
    
      
    
  


The purpose of this is to mimic the gadget API. Applications can choose to listen for events on the window object with the listed event name and receive the corresponding property value. This is made possible because of a shared library that is preloaded into applications by the gadget provider using the LD_PRELOAD environment variable. This preloaded library intercepts calls to create EFL window objects and converts Wayland protocol events into callbacks on the window object. Gadget application developers can then write applications without the need to concern themselves with anything related to Wayland.

Example EFL Gadget Sandbox

An example sandbox gadget written in EFL reads something like this:

#include < Elementary.h >
static Evas_Object * popup;
static Evas_Object * child;

static void
popup_del(void * data EINA_UNUSED, Evas * e EINA_UNUSED, Evas_Object * obj EINA_UNUSED, void * event_info EINA_UNUSED)
{
  popup = NULL;
}

static void
child_del(void * data EINA_UNUSED, Evas * e EINA_UNUSED, Evas_Object * obj EINA_UNUSED, void * event_info EINA_UNUSED)
{
  child = NULL;
}

static void
popup_unfocus(void * data EINA_UNUSED, Evas * e EINA_UNUSED, Evas_Object * obj, void * event_info EINA_UNUSED)
{
  evas_object_del(obj);
}

static void
mouse_button(void * data EINA_UNUSED, Evas * e EINA_UNUSED, Evas_Object * obj, void * event_info)
{
  Evas_Object * ic;
  Evas_Event_Mouse_Down * ev = event_info;
  char buf[PATH_MAX];
  int w, h;
  Evas_Object * win;
  Elm_Win_Type type = ELM_WIN_POPUP_MENU;
  if ((ev - > button != 1) && (ev - > button != 3)) return;
  if (ev - > button == 3)
  {
    type = ELM_WIN_BASIC;
    if (child)
    {
      evas_object_del(child);
      return;
    }
  } else
  {
    if (popup)
    {
      evas_object_del(popup);
      return;
    }
  }
  win = elm_win_add(elm_win_get(obj), "win", type);
  elm_win_alpha_set(win, 1);
  if (ev - > button == 3)
  {
    child = win;
    evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, child_del, NULL);
  } else
  {
    popup = win;
    evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, popup_del, NULL);
  }
  if (ev - > button == 3)
    evas_object_event_callback_add(win, EVAS_CALLBACK_FOCUS_OUT, popup_unfocus, NULL);
  ic = elm_icon_add(win);
  snprintf(buf, sizeof(buf), "%s/images/bubble.png", elm_app_data_dir_get());
  elm_image_file_set(ic, buf, NULL);
  elm_image_object_size_get(ic, & w, & h);
  evas_object_size_hint_aspect_set(win, EVAS_ASPECT_CONTROL_BOTH, w, h);
  if (ev - > button == 1)
  {
    elm_image_resizable_set(ic, EINA_FALSE, EINA_FALSE);
    elm_image_no_scale_set(ic, EINA_TRUE);
  }
  evas_object_size_hint_weight_set(ic, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
  evas_object_size_hint_fill_set(ic, 0.5, 0.5);
  evas_object_size_hint_min_set(ic, 100, 100);
  elm_win_resize_object_add(win, ic);
  evas_object_show(ic);
  evas_object_show(win);
}

int
main(int argc, char * argv[])
{
  Evas_Object * win, * ic;
  char buf[PATH_MAX];
  int w, h;
  elm_init(argc, (char * * ) argv);
  elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
  elm_app_info_set(main, "elementary", "images/logo.png");
  win = elm_win_add(NULL, "icon-transparent", ELM_WIN_BASIC);
  elm_win_title_set(win, "Icon Transparent");
  elm_win_autodel_set(win, EINA_TRUE);
  elm_win_alpha_set(win, EINA_TRUE);
  ic = elm_icon_add(win);
  snprintf(buf, sizeof(buf), "%s/images/logo.png", elm_app_data_dir_get());
  elm_image_file_set(ic, buf, NULL);
  elm_image_object_size_get(ic, & w, & h);
  evas_object_size_hint_aspect_set(win, EVAS_ASPECT_CONTROL_BOTH, w, h);
  if (argc > 1)
  {
    elm_image_resizable_set(ic, EINA_FALSE, EINA_FALSE);
    elm_image_no_scale_set(ic, EINA_TRUE);
  }
  evas_object_size_hint_weight_set(ic, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
  evas_object_size_hint_fill_set(ic, 0.5, 0.5);
  evas_object_size_hint_min_set(ic, 100, 100);
  elm_win_resize_object_add(win, ic);
  evas_object_show(ic);
  evas_object_event_callback_add(ic, EVAS_CALLBACK_MOUSE_DOWN, mouse_button, NULL);
  evas_object_show(win);
  ecore_main_loop_begin();
  return 0;
}

This code results in an E logo gadget like this:

How to Create an EFL Gadget Sandbox - gadget_sandbox1.png

Left clicking creates a popup window (xdg_popup) which the gadget provider extracts from the compositor widget to display like a normal gadget contextual popup.

How to Create an EFL Gadget Sandbox - gadget_sandbox2

Right clicking creates a child window (xdg_toplevel with parent) which the gadget provider extracts from the compositor widget to display like a standard gadget popup.

How to Create an EFL Gadget Sandbox - gadget_sandbox3

There are no toolkit or language restrictions for gadget applications using this functionality. The only requirement is that interacting with the compositor in any way, such as receiving the gadget properties, requires use of the Wayland protocol. Even without that, any framework or toolkit that has Wayland support can now have fully functional gadgets running inside Enlightenment.

The sandbox infrastructure can currently be found in the desksanity module, and is scheduled to be merged into the main tree after the E22 release.

Building Technical Talent in the Enlightenment Community by Solving Bugs

The past week or so has seen a significant amount of progress in the gadget backend of Enlightenment, due in no small part to the constant poking and prodding from Stephen Houston: our newest Samsung OSG Intern. As he mentioned in his post, we’ve known each other for quite some time now, so mentoring him has allowed both of us to skip over most of the pleasantries and get down to the code.

Establishing a Mutually-Beneficial Partnership

This type of internship is certainly new to me; seldom do I get the opportunity to sponsor and mentor a member of the community who has already been a contributor for such a long time. Given that I’d been the only one to use the new gadget API released in Enlightenment v21, I was curious to see what others would think after spending some time developing on top of it; soliciting feedback on the API during its development, as I did during my initial development work, hardly substitutes for the experience of spending time working with it in depth.

Before starting on his main gadget projects, Stephen decided he would try to adapt an existing gadget to the new API to test it out. We discussed this for a bit, and agreed it was a sound idea, so he set off to work. I think the first time I heard back from him was a few days after (keep in mind that he only works on this part-time in addition to his real job), and he was nearly done. He had found the new API to be satisfactory and easy to use, which was certainly a relief to me! This was, however, the point at which he brought up the sizing issue he posted a video of. We managed to work through it after a brief discussion of the problem, and he went on to tackle the last aspect of the gadget conversion: drag-n-drop (DND).

Solving Technical Problems While Growing Community Talent

Anyone who knows me or my past blogging knows that DND is not one of my favorite things to work on. While I typically have some choice words to describe it, the fact is that it’s a challenging subsystem to work within due to the necessary message passing between the drag initiator (the user) and the drop receiver. This leads to multiple places requiring debugging and testing anytime problems occur, adding to the complexity of solving problems. Solving DND bugs in a given area, however, is something like a rite of passage, and the tasks can only get easier from there… usually.

The DND issues here were numerous, and it turned out they were all either gadget infrastructure bugs or internal compositor bugs; in other words, I should be solving them to allow Stephen to continue progressing with his own work. I believe this is one of the great benefits of the way we’re running the internship program: in addition to sponsoring community members to continue doing the great work they have difficulty finding time for, we’re also providing the manpower behind the scenes to support that work and allow them to progress at their own pace, minimally hindered by any bugs outside the direct scope of their projects.

At this point, testing has been completed and Stephen’s new pager gadget has been merged into the upstream repository and is available for general use. He’s now already quite deep into the brand new launcher gadget, and I think we’ll have some exciting info to post about that soon. Stay tuned for more updates, as we have some new members of the Samsung OSG Internship program starting their projects within the next couple weeks!

A Venture Into Enlightenment’s Gadget API

In my last post, I mentioned that my internship would revolve around creating gadgets using the new Enlightenment gadget API. After several conversations with Mike Blumenkrantz, the creator of the gadget API and my mentor for this internship, we determined the Pager module is in a state that requires minimal work to be converted to the new gadget API. Therefore, converting Pager to the new API would allow me to focus on learning how the new gadget system works better than writing a gadget from the ground up.

A Venture Into Enlightenments Gadget API - pager_higher_res
The Pager Enlightenment Module

Mentors are a Great Resource for Learning Something New

Before I continue with details about the Pager API conversion, I think it would be prudent to explain how this internship works behind the scenes. Mike and I have known each other for quite a while as we’ve both worked on Enlightenment and EFL for years. In fact, when Mike joined the project, I was one of the first to welcome him and encourage his development, and he has quickly rose to the top of the project by bringing creative ideas and elegant code. This relationship is what my internship is all about: I get to work with people within the community that I’ve already spent considerable time with. Additionally, I get to build new relationships in the open source industry and meet mentors who are well adjusted to how open source development works.

Rarely do you find internships that are more of a partnership, but that’s what the Open Source Group’s internship offers. Mike and I keep an open line to each other to frequently discuss ideas, project directions, and bugs that we have found in both my own code as well as his. The ability to get in touch with a mentor quickly to solve problems and keep development moving is extremely valuable, and I get to play an active role in the decision making process.

Converting Pager to the New Gadget API

Now that I’ve provided context, I’ll explain one of the first things I’ve worked on as a part of this internship. The rest of this article will provide a brief overview of the process of converting Pager to the new gadget API. If you have any experience with GUI application development, then you will feel like you already know the new gadget API. It works the way most graphic toolkits do: create an object, tell the object how it should be laid out, and let the backend do the actual layout, sizing, and event handling. There are exceptions, of course, where size or events need to be handled manually; the gadget API makes this seamless with simple set/get functions and event callbacks.

Pager creates a simple table and packs layout objects that mimic virtual desktops into the table. The objects are packed based on the virtual desktop layout, and sizing is based on a gadget site. You can think of a gadget site as a container, or parent, for the object. Pager handles sizing based on the orientation of its parent gadget site. This orientation can be determined from a simple get function. If the gadget site has a horizontal orientation, then Pager will create a layout based on the height of the gadget site. If the gadget site has a vertical orientation, then Pager will create a layout based on the width of the gadget site. Let’s use Pager as an example of how sizing can be handled in a gadget.

Once the height or width (depending on your orientation) has been determined, you can use that size, the aspect ratio of the desktop, and the number of virtual desktops. Let’s assume the desktop has an aspect ratio of 16:9 and there are 4 virtual desktops. These virtual desktops are laid out horizontally in a row. The gadget site (parent of the Pager) has a horizontal orientation and height of 49 pixels. We can use a simple equation where we solve for x to determine what the width of the Pager should be: x = (aspect width / aspect height) * gadget height. This formula is a simple version of 16/9 = x/y where x is your desired width and y is your desired height. Since we already know the desired height is 49, plug 49 into the equation for gadget height. We also know the aspect width is 16 and the aspect height is 9, so we can plug those numbers into the formula as well. That gives us x = (16/9)*49. Solving for x = 87 pixels (rounded). You now know that each virtual desktop layout in the Pager will need to be 87 pixels wide and 49 pixels tall. Since there are four virtual desktops, and they are laid out horizontally, you can determine that 4 * 87, or 348 pixels, will be the width of the Pager, and 49 pixels will be the height. Obviously the math gets slightly more complicated if you have virtual desktops laid out in two directions, both horizontally and vertically, but the principle is the same. If the gadget site is resized, or the number of virtual desktops changes, the size of the Pager is updated simply using the formula we just established.

Once you have the hang of how sizing and orientation works with the gadget API, you can then begin to work on the contents of your actual gadget. Remember, learning new things take some trial and error, and no one is perfect. Sizing can be tricky, but the new gadget API makes it as easy as possible. If you don’t believe me, watch the following video and you can see that even I didn’t get sizing correct the first go around. Notice the Pager jumping around because it is constantly changing sizes.

Furthermore, remember that any issues you may find are not necessarily your fault because the gadget API is new. One of the biggest roadblocks I faced when completing the Pager conversion was correcting drag n drop. I spent a lot of time debugging my own code and asking Mike to debug his as well. Finally, Mike found a bug in the way the gadget code handles layering. He promptly made the fix and I was able to continue working to complete the Pager. It’s rare as an intern to get to ask your mentor to fix HIS bugs! :-).

Once the Pager was working well, the final touch was to create a configuration popup. In the past, Enlightenment modules have used window dialogs to handle configuration. The idea with the new gadgets is to have a simple popup that appears when a gadget is right clicked.

A Venture Into Enlightenments Gadget API - pager_popup
The Pager gadget configuration popup

The end result that is now available via git in Enlightenment can be seen in the following video:

Converting Pager to the new gadget API was a great primer for me to learn and prepare myself for the rest of my projects in this internship. The next project I will be working on is creating a new launcher for the enlightenment desktop. I will be writing this gadget from scratch and it will serve not only as a launcher, but also as a taskbar with the ability to handle iconified windows. Keep an eye out for updates regarding my progress!

Introducing Stephen Houston: Our Newest Intern

As the newest developer to have the privilege of taking part in Samsung’s Open Source Group internship program, I would like to give a brief introduction of myself, my experience, and my focus with Samsung.

I am a software developer and analyst who holds a bachelor’s degree in Computer Information Systems, and I’m currently working towards a Master of Business Administration. I’ve been an open source developer since I was 16 (a long time ago), and I have spent the majority of my time writing code related to the Enlightenment project. When I first stumbled across Enlightenment 17 in the early 2000’s, there was a widget library at the time called Ewl. The creator of Ewl, Nathan Ingersoll, took me under his wing and began teaching me C and how to use Ewl and other Enlightenment Foundation Libraries (EFL). I used this knowledge to create and develop Ephoto: an EFL based image viewer. I have made many contributions to the EFL including work that can be seen on Enlightenment’s file manager and file selector, Elementary widgets including gengrid, and Eio’s asynchronous file operations.

Ephoto is a fast, visually appealing image viewer that has many features. It allows users to view their images by a thumbnail grid, one by one, or in a moving slideshow. Ephoto also has editing filters that include auto equalization, gaussian blur, difference of gaussian sharpening, Floyd Steinberg dithering, Sobel edge detection, embossing, red eye removal, posterizing, sketching, and more. Brightness, contrast, gamma, hue, saturation, and value levels can all be adjusted on images as well. Ephoto is currently experiencing it’s first release cycle after over a decade of development (Sound familiar? Perhaps like E17?).

As part of my work with the Samsung Open Source Group I’ll be using my experience and expertise with the Enlightenment project to create gadgets using the new gadget API Mike Blumenkrantz introduced in the Enlightenment 21 release. My primary focus will be to create new modules that will be compatible with Bryce, a container infrastructure that Mike wrote for the new gadget system. Specifically, these gadgets will include a new launcher for Enlightenment, as well as gadgets for system information. The new launcher gadget will also have taskbar functionality, popup window previews, and .desktop context action menus. The system info gadgets will cover an array of information including battery percentage, power saving options, temperature, CPU frequency, CPU load, memory usage, and network upload/download statistics. All of the new gadgets will provide an intuitive, elegant interface, with configuration options presented in a popup rather than a dialog so they are consistent with the other gadgets that currently use the new API. My goal is to create these gadgets in a manner that is easily replicated in order to encourage others to get involved and create more gadgets. When I am finished, there will be enough stable modules to make Bryce a legitimate replacement for the current shelf system.

It is my pleasure to be given this internship to work with other open source enthusiasts and spend more time on a project that I have poured myself into for the last decade. I look forward to communicating with all of you as we work on these projects and work with the open source community. Long live open source development!

Enlightenment Gadget Lifetime Management & Site Creation

This article is part of a series of tutorials about Enlightenment: a compositing and stacking window manager. The previous tutorials covered the basics of gadgets, this article will explore some of the more complex aspects in more detail, specifically lifetime management and gadget site creation.

Gadget Lifetime Management

Gadgets are bound by two lifetimes: the gadget object’s lifetime, and the gadget instance lifetime.

The gadget object is the visible display for a gadget and it is deleted when either the site is deleted, when the gadget instance is deleted, or when the gadget’s orientation changes. This lifetime can be tracked using the EVAS_CALLBACK_DEL callback on the created object. At the time of calling, this indicates that any memory related to the gadget object should be cleaned up, and any non-Elementary sub-objects should be deleted; the toolkit will not delete these automatically and they will leak without manual deletion. Once this callback has been triggered, the gadget object is dead and there will be no further calls made upon it.

The gadget instance is a sort of configuration struct for a gadget. It can be anything from a clock gadget’s time format to the wireless network a gadget is configured to connect to. The “gadget_removed” smart callback can be used on the gadget site object to terminate its lifetime. This callback occurs with the event_info parameter set as the gadget’s object when the user removes the gadget instance and typically means the gadget configuration should be deleted. However, it’s possible for gadgets to share the same configuration across multiple instances depending on how the gadget code is written. Here’s a sample “gadget_removed” callback from the clock gadget.

static void
 _clock_gadget_removed_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
 {
 Instance *inst = data;

if (inst->o_clock != event_info) return;
 time_config->items = eina_list_remove(time_config->items, inst->cfg);
 eina_stringshare_del(inst->cfg->timezone);
 eina_stringshare_del(inst->cfg->time_str[0]);
 eina_stringshare_del(inst->cfg->time_str[1]);
 E_FREE(inst->cfg);
 }

The callback filters out occurrences for other gadgets then deletes the configuration for the appropriate gadget instance, ensuring the gadget has been removed. Take note that this callback is called before the EVAS_CALLBACK_DEL callback, meaning any structs needed for that callback should not be freed during the “gadget_removed” callback.

Gadget Site Creation

Gadget site creation involves placing gadget sites on alternative objects; there are currently two methods for adding new gadget sites:

E_API Evas_Object *e_gadget_site_add(E_Gadget_Site_Orient orient, const char *name);
 E_API Evas_Object *e_gadget_site_auto_add(E_Gadget_Site_Orient orient, const char *name);

The former, e_gadget_site_add(), directly adds a new gadget site to the compositor canvas using the give “name” parameter as the site’s ID; the site can then be manipulated as a normal object. Calling the function again with the same name will create the same site with the same configuration. However, it’s important to note that the gadget site will not remember its geometry or stacking when using this, so ideally only container objects should use it, such as the Bryce container object here.

The latter, e_gadget_site_auto_add(), adds a configuration for a gadget site that’s automatically created and managed. The “name” parameter in this case should match the name of a compositor object (extant or not). This method will cause a gadget site to be created and positioned automatically to cover the named compositor object every time the target object is created; it will also be destroyed if the target object is deleted. This can be used to avoid the requirement that an object manages its own gadget site when the stacking and geometry would be used in this manner. As an example, this method is used to handle the editing of desklock gadgets:

static Eina_Bool
 _gadget_desklock_handler(void *d EINA_UNUSED, int t EINA_UNUSED, E_Event_Comp_Object *ev)
 {
 E_Notification_Notify n;
 const char *name;
 Evas_Object *site, *comp_object;

name = evas_object_name_get(ev->comp_object);
 if (!name) return ECORE_CALLBACK_RENEW;
 if (strncmp(name, "desklock", 8)) return ECORE_CALLBACK_RENEW;
 evas_object_layer_set(ev->comp_object, DESKLOCK_DEMO_LAYER - 1);
 site = e_gadget_site_auto_add(E_GADGET_SITE_ORIENT_NONE, name);
 evas_object_smart_callback_add(site, "gadget_moved", _gadget_moved, NULL);
 evas_object_layer_set(site, DESKLOCK_DEMO_LAYER);
 comp_object = e_gadget_site_edit(site);
 e_comp_object_util_del_list_append(ev->comp_object, comp_object);
 e_comp_object_util_del_list_append(ev->comp_object, desklock_rect);

memset(&n, 0, sizeof(E_Notification_Notify));
 n.timeout = 3000;
 n.summary = _("Lockscreen Gadgets");
 n.body = _("Press Escape or click the background to exit.");
 n.urgency = E_NOTIFICATION_NOTIFY_URGENCY_NORMAL;
 e_notification_client_send(&n, NULL, NULL);
 return ECORE_CALLBACK_RENEW;
 }

When the dummy desklock object is created, a gadget site will be created to cover it and further begin editing on it. However, note the important part here is this line:

site = e_gadget_site_auto_add(E_GADGET_SITE_ORIENT_NONE, name);

This will create the configuration for the named desklock object, ensuring that gadget internals will create the same gadget site any time the desklock object exists.
Using either of these functions, it’s possible to easily create a gadget site at any location, and have it either self-manage or perform management using other objects. This should allow gadgets to become more ubiquitous within the desktop, making them simple to place into new locations such as menus or window borders.

This concludes the tutorials for gadget lifetime management and creating new gadget sites. Stay tuned for more Enlightenment tutorials!

How Enlightenment Gadgets Handle Sizing

This tutorial will provide further detail about aspects of Enlightenment’s new gadget system. Specifically, it will explore how sizing works in different contexts and how simple sizing policies can be leveraged to provide the best view of a gadget. Let’s start with the basics: what is sizing and why does it matter?

Gadgets work a bit different than typical application widgets where one would simply pack them into a layout or use WEIGHT and ALIGN hints to fill portions of available regions. A gadget site uses an automatic sizing algorithm to fit itself into its given location. This ensures that gadgets are always the size the user has specified while also maintaining the best sizes for the gadgets so they will look the way the author intended. Finally, it also greatly simplifies the work of gadget authors since the need to calculate exact sizes and continually adjust based on available size is no longer applicable.

Example of Gadget Sizing

Looking at the relevant code from the “Start” gadget, the gadget uses the following line for its sizing:

evas_object_size_hint_aspect_set(o, EVAS_ASPECT_CONTROL_BOTH, 1, 1);

This informs the gadget site that the gadget should always maintain a 1:1 aspect for width:height, ensuring that the correct amount of space is allocated for the gadget regardless of the size at which it is displayed in a given container. The resizing can be seen in action here.

Taking a slightly more complicated case, let’s look at the clock gadget from the same video. The following function controls its sizing:

static void
_eval_instance_size(Instance *inst)
{
   Evas_Coord mw, mh;
   int sw = 0, sh = 0;
   Evas_Object *ed = elm_layout_edje_get(inst->o_clock);

   edje_object_size_min_get(ed, &mw, &mh);

   if ((mw < 1) || (mh < 1))
     {
        if (edje_object_part_exists(ed, "e.sizer"))
          {
             edje_object_part_geometry_get(ed, "e.sizer", NULL, NULL, &mw, &mh);
          }
        else
          {
             Evas_Object *owner;

             owner = e_gadget_site_get(inst->o_clock);
             switch (e_gadget_site_orient_get(owner))
               {
                case E_GADGET_SITE_ORIENT_HORIZONTAL:
                  evas_object_geometry_get(owner, NULL, NULL, NULL, &sh);
                  break;

                case E_GADGET_SITE_ORIENT_VERTICAL:
                  evas_object_geometry_get(owner, NULL, NULL, &sw, NULL);
                  break;

                default: break;
               }

             evas_object_resize(inst->o_clock, sw, sh);
             edje_object_message_signal_process(ed);

             edje_object_parts_extends_calc(ed, NULL, NULL, &mw, &mh);
          }
     }

   if (mw < 4) mw = 4;
   if (mh < 4) mh = 4;

   if (mw < sw) mw = sw;
   if (mh < sh) mh = sh;

   evas_object_size_hint_aspect_set(inst->o_clock, EVAS_ASPECT_CONTROL_BOTH, mw, mh);
}

Breaking the Code Down

The normal path for this code to take is to get the size of the layout using edje_object_size_min_get(), and then set that as the aspect for the gadget. This will trigger when the digital clock changes its face, ensuring that all the digits receive adequate space. In the case where this size has not been calculated yet, two alternate paths are provided for creating an aspect for the gadget.

In the first path, a check for the “e.sizer” part in the layout is performed and the size of this part is used if the part exists. This is a theme-defined part that may or may not exist, so checking to see which case is currently in use is important before attempting to use this size.

If this check fails, the gadget retrieves the size of the gadget site on its non-extending axis, then resizes itself to this size and forces a recalculation to make an attempt at accurate sizing. Note: in this case it is usually guaranteed that another recalculation will be triggered soon after, so this is likely to only be a temporary size.

Regardless of the method used, an aspect hint is set for the gadget that the gadget site can then use to handle any resizing that may occur. Since the gadget receives events any time a gadget site resize occurs, it’s possible for a gadget to change its aspect hints at any time to re-aspect itself and take up more or less space depending on various scenarios.

This wraps up the mechanics of gadget sizing, but stay tuned for even more gadget-related tutorials in the future!

How to Create Enlightenment Gadgets

Creating desktop widgets, aka “gadgets,” has never been easier for Enlightenment enthusiasts than it is after the E21 release. The new E_Gadget system provides an updated API for integrating objects into the compositor, removing most of the overhead from the E_Gadcon system. This makes writing gadgets nearly identical to ordinary application writing. This post will serve as an introduction on the topic of writing gadgets with a focus on the basics; it will use the Start gadget as a reference.

How to Create a Gadget

The first step to integrating a new gadget is to add the new gadget type to the subsystem so the user can access it. This is done with the following function:

void e_gadget_type_add(const char *type, E_Gadget_Create_Cb callback, E_Gadget_Wizard_Cb wizard)

This function coincides with related callbacks:

Evas_Object *(*E_Gadget_Create_Cb)(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient);

void (*E_Gadget_Wizard_Cb)(E_Gadget_Wizard_End_Cb cb, void *data);

Using e_gadget_type_add, a developer can implement gadgets of type, calling callback to create the gadget object, and optionally providing wizard callback to run configuration options for the user before creating a new gadget. Gadget types can be added and removed at any time; adding a type will populate any existing gadgets for that type, and removing a type will similarly destroy any existing instances of that gadget. No additional cleanup is needed for gadget objects when removing a gadget type.

The wizard callback will be discussed in a subsequent article, but for now let’s look at a gadget creation callback.

EINTERN Evas_Object *
start_create(Evas_Object *parent, int *id EINA_UNUSED, E_Gadget_Site_Orient orient)
{
   Evas_Object *o;
   Instance *inst;

   inst = E_NEW(Instance, 1);

   o = elm_layout_add(parent);

   e_theme_edje_object_set(o, NULL, "e/gadget/start/main");
   elm_layout_signal_emit(o, "e,state,unfocused", "e");

   inst->o_button = o;
   evas_object_size_hint_aspect_set(o, EVAS_ASPECT_CONTROL_BOTH, 1, 1);

   evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN,
                                  _button_cb_mouse_down, inst);
   evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, start_del, inst);
   evas_object_smart_callback_add(parent, "gadget_site_anchor", _anchor_change, inst);
   evas_object_smart_callback_add(parent, "gadget_created", _gadget_created, inst);
   do_orient(inst, orient, e_gadget_site_anchor_get(parent));

   return o;
}

Breaking the Gadget Down

This function returns an object that is used as a gadget with various policies and utilities applied to it. When this object is deleted, the gadget instance is considered destroyed and any related cleanup should be performed at this time. The creation function above hooks the EVAS_CALLBACK_DEL callback

A parent object is passed to the create function. This object can be considered the “owner” of the gadget, and its lifetime is guaranteed to exceed the lifetime of the gadget object. The parent object is the gadget site.

The id parameter refers to the specific instance of a gadget that should be used. It is passed as a pointer to the id so the gadget can change the returned id if necessary. The gadget subsystem uses ids as follows:

  • If the passed id is < 0, the created gadget will be used for demo purposes only, (e.g., a gadget site configuration wizard), and optimal display configurations should be applied to make the gadget look neat.
  • If the id is 0, the gadget should create a new config instance to use for this gadget object and set the id parameter to this value.
  • If the id is > 0, the created gadget should use the config instance which corresponds to the passed id.

Multiple instances of a gadget can exist using the same config instance.

Lastly, the orient parameter determines the orientation of the gadget. The orientation of a gadget matches the orientation of its parent site at all times, and it is guaranteed that the orientation of a gadget object will not change during that object’s lifetime.

Continuing through the function, a layout object gets created with the “e/gadget/start/main” theme applied to it. This object then has a 1:1 aspect hint set. Aspect hints are utilized for the E_Gadget system to handle all sizing; a gadget should never need to manually size itself or set its own explicit size hints. Sizing is handled automatically based on the aspect the gadget has set and then the gadget site’s own sizing policies.

A “gadget_site_anchor” callback is set here. This callback is triggered any time the anchor of a gadget site changes. Anchors indicate the place where the gadget site attaches to something, if such an attachment exists. For example, a rectangular gadget container on the left side of the screen would be considered as being anchored to the left side of the screen. This anchoring can be used in conjunction with orientation to present the user with a more detailed view of a gadget in some cases.

Finally, a “gadget_created” callback is set. This callback is triggered after the creation of any gadget on the site, and it can be used to catch cases where the gadget site changes its anchoring during the startup of Enlightenment.

Returning the created object at this point will yield a displayable gadget that the subsystem manages. It will be created automatically on the owner gadget site every time the site populates, and it will be appropriately destroyed either through site destruction or gadget type removal.

This concludes the introduction of gadget development in E21. Stay tuned for more tutorials in the future!