(Classic) Lesson: Pico to Pico Subscriptions

Learning Objectives

After completing this lesson, you will be able to do the following:

  • Understand pico-to-pico relationships and subscriptions
  • Use the developer tools to manage pico subscriptions
  • Link two or more picos with a subscription programmatically and have them interact. 

Prerequisites 

You should have already completed  the following lessons:

Subscriptions

Subscriptions play an important role in building systems of picos. In the lesson on Pico-based Systems, you created child picos. If the parent-child relationship was the only one that existed, we could create hierarchies of picos, but not peer-to-peer systems of picos. Subscriptions allow us to establish relationships between any two picos. 

A subscription represents a relationship between two picos. It has a name and two channels, one from each pico participating in the subscription to the other. Each side in the subscription can be given a role to further identify the purpose of the subscription. 

Subscriptions Using Devtools

To begin, let's use Devtools to create a subscription between the Thermostat and TempSensor picos we created in the Pico-based System lesson. 

First, we navigate to the Thermostat pico and click on the "Subscription" button. The click the "New Subsciption" button and you'll see a screen like this:

I've named the subscription "Temperature" and put it in the "Closet" namespace. I left channel type blank to accept the default. My role is a "Thermostat" and the role I want the subscriber to play is "Temperature Sensor". The Target is the ECI of the pico we want to subscribe this pico to. Note that we have to have an ECI to the pico we're creating the subscription with. 

After clicking "Subscribe", we see the outbound subscription in the list of pending outbound subscriptions:

Creating the pending outbound subscription the Thermostat pico, also creates an pending inbound subscription in the TempSensor pico. If we navigate to the TempSensor pico and look at the subscriptions, we see this:

Note that in the TempSensor pico we have the option to approve or reject this subscription request. If we click "Approve" the inbound pending subscription goes away and we have a new subscription in our subscription list:

 

Navigating to the Thermostat pico and look at its subscriptions, we see that the outbound pending subscription has also been moved to the subscriptions list:

Clicking "Unsubscribe" either here or in the TempSensor pico would delete the subscriptiion from both. 

Managing Subscriptions Programmatically

Wrangler provides functions, actions, and rules for managing subscriptions from within your KRL code. 

To begin, I created a new ruleset called child_subscriptions, registered it, and installed it in the TempSensor and Thermostat picos I created earlier.

Listing Subscriptions

First, let's write a function to show us our subscriptions. The Wrangler function subscriptions() does that and we could call it directly since Wrangler is installed in the pico, but let's wrap it in our own function and remove the status message. Usually, you'd be doing further processing on the subscription list. Here's our function to just return the subscriptions in the Closet namespace:

subs = function() {
  subs = wrangler:subscriptions(null, "name_space", "Closet");
  subs{"subscriptions"}
}

Using the Sky Cloud API to query this function in the Thermostat pico provides the following output:

{"Closet:pickaway": {
    "name_space": "Closet",
    "relationship": "Temperature Sensor<->Thermostat",
    "status": "subscribed",
    "channel_name": "Closet:pickaway",
    "inbound_eci": "0AF50B24-4F99-11E6-98E8-D4E9E71C24E1",
    "subscriber_role": "Thermostat",
    "my_role": "Temperature Sensor",
    "attributes": "status",
    "subscription_name": "Temperature",
    "outbound_eci": "0A053108-4F99-11E6-A902-F9F0E71C24E1"
    }
}

This is essentially the same data we see in Devtools. 

Remember that for this to work, we have to have used the wrangler module, aliasing it to wrangler, set sharing on, and provided the module.

Introducing the Thermostat to the TempSensor

Let's build a rule that allows a pico to introduce itself to another pico. Since it's in our Closet collection, we'll default some of the normal attributes required in a subscription. And since this is a self-introduction, we can raise the wrangler:subscription event on the internal event bus:

rule introduce_myself {
  select when pico_systems introduction_requested
  pre {
    sub_attrs = {
	  "name": event:attr("name"),
	  "name_space": "Closet",
	  "my_role": event:attr("my_role"),
	  "subscriber_role": event:attr("subscriber_role"),
	  "subscriber_eci": event:attr("subscriber_eci")
    };
  }
  if ( not sub_attrs{"name"}.isnull()
    && not sub_attrs{"subscriber_eci"}.isnull()
     ) then
  send_directive("subscription_introduction_sent")
    with options = sub_attrs
  fired {
    raise wrangler event "subscription" attributes sub_attrs;
    log "subcription introduction made"
  } else {
    log "missing required attributes " + sub_attr.encode()
  }
       
}

 

We can use the Sky Event Console to raise the pico_system:introduction_requested event with the proper attributes. We're raising this event in the TempSensor pico and giving it an ECI for the Thermostat pico in the subscriber_eci attribute. 

Once we've done this, we see a new pending outbound subscription for the TempSensor in the list of subscriptions returned by our subs() function with the attributes we supplied:

{
	"Closet:monosymptomatic": {
		"name_space": "Closet",
		"relationship": "subscription_requestor<->subscription_provider",
		"status": "outbound",
		"channel_name": "Closet:monosymptomatic",
		"inbound_eci": "2E3ACDAA-5050-11E6-A09C-9EB1E71C24E1",
		"subscriber_role": "subscription_provider",
		"my_role": "subscription_requestor",
		"subscriber_eci": "EA88F554-485D-11E6-9F7F-47DDE71C24E1",
		"attributes": "status",
		"subscription_name": "flopper",
	}
}

Note that the status is "outbound." There is a complementary inbound subscription in the Thermostat. 

Once an introduction has been made and the subscription is pending, it can be approved or rejected.

Some application may allow automatic approval of pending subscriptions. This cookbook example shows how to write a rule that auto-approves subscriptions. Generally, promiscuously approving pending subscriptions is a security risk, so most auto-approvals take place where something about the subscription requestor can be verified. For example, a child might be programmed to automatically approve subscription requests from its parent, after checking that the invitation came in on a channel that can be verified as being from the parent. 

In our case, we will use a rule that responds to an explicit event. 

 rule approve_subscription {
    select when pico_systems subscription_approval_requested
    pre {
      pending_sub_name = event:attr("sub_name");
    }
    if ( not pending_sub_name.isnull()
       ) then
       send_directive("subscription_approved")
         with options = {"pending_sub_name" : pending_sub_name
  		                }
   fired {
     raise wrangler event "pending_subscription_approval"
           with channel_name = pending_sub_name;
     log "Approving subscription " + pending_sub_name;
   } else {
     log "No subscription name provided"
   }
}

This rule uses the sub_name attribute to identify the pending subscription to approve and then, provided it's not null, raises the wrangler:pending_subscription_approval event. Wrangler does everything else. 

We raise the pico_systems:subscription_approval_requested event in the pico that was invited to subscribe (where the approval has to take place). In this case, we raise it to the Thermostat like so:

If we look at the subscriptions in the TempSensor again, we'll see that the event is no longer pending.  Specifically, it's status has changed from "outbound" to "subscribed."

{"Closet:monosymptomatic": {
	"name_space": "Closet",
	"relationship": "subscription_requestor<->subscription_provider",
	"status": "subscribed",
	"channel_name": "Closet:monosymptomatic",
	"inbound_eci": "2E3ACDAA-5050-11E6-A09C-9EB1E71C24E1",
	"subscriber_role": "subscription_provider",
	"my_role": "subscription_requestor",
	"subscriber_eci": "EA88F554-485D-11E6-9F7F-47DDE71C24E1",
	"subscription_name": "flopper",
	"attributes": "status",
	"outbound_eci": "2F18D3C0-5050-11E6-ADB4-FAA4E71C24E1"
	}
}

Similarly, the subscriptions in the Thermostat pico would show the status has changed from "inbound" to "subscribed. 

Wrangler does much of the heavy lifting in managing subscriptions. The introduce_myself rule simply stated that a subscription was needed and Wrangler not only created the pending outbound subscription for the TempSensor, but also sent an event to the subscription provider, Thermostat, so that a pending inbound subscription could be created there. Then, when we approved the subscription in Thermostat, Wrangler again, made the job easy. Wrangler made the pending inbound subscription into a subscription, created a channel for the subscriber (TempSensor) to use, sent an event to the subscriber with the new channel, and changed the subscription in TempSensor from outbound. 

Deleting a Subscription

We delete a subscription between two picos in a similar manner. The following rule shows how to use Wrangler to do that:

rule remove_subscription {
  select when pico_systems subscription_deletion_requested
  pre {
    pending_sub_name = event:attr("sub_name");
  }
  if ( not pending_sub_name.isnull()
     ) then
       send_directive("subscription_approved")
         with options = {"pending_sub_name" : pending_sub_name }
 fired {
   raise wrangler event "subscription_cancellation"
         with channel_name = pending_sub_name;
   log "Approving subscription " + pending_sub_name;
 } else {
   log "No subscription name provided"
 }
}

After determining that it has a non-null name for the subscription, the rule raises the wrangler:subscription_cancellation event with the correct channel name. Wrangler not only deletes the subscription in the pico where this event is raised, along with it's attendant channel, but also signals to the partner pico in the subscription that it should do the same.  As a result the subscription is completely removed from both picos. 

 

 

 

Copyright Picolabs | Licensed under Creative Commons.