Code for "Make it Look Like..." -- watch and learn patterns?


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy) #1

I just found the “Make it Look Like…” SmartApp under Safety & Security.

Is the source code for this available?

Reading various device states at multiple times to build an automatic simulated schedule? Sounds easy enough, so I could try from scratch… but there are enough possible variations on this that getting the code would be nice.

Thanks, …?
…CP.


(Zach Naimon) #2

Here is some code for a single switch make it look like. Install it multiple times with multiple switches.


/**
 *  Make It Look Like
 *
 *  Author: SmartThings
 */

preferences {
	section("Utilize these swithces") {
		input "switches", "capability.switch"
        
	}
}

def installed()
{
	initialize()    
}

def updated()
{
	unsubscribe()
	initialize()
}

def initialize(){
	def freq = 10
	schedule("0 0/$freq * * * ?", runJob)
    def day = new Date(now())
    def midnight = timeTodayAfter(day, "00:00", location.timeZone)
    runDaily(midnight, newDay)
    
}

def newDay(){
	state.daysBefore = (new Random().nextInt(4)) * 7
    state.daysBeforeMS = state.daysBefore * 86400000
}
		
def runJob() {	
    def startTime = now() - state.daysBeforeMS
    def endTime = startTime + 600000
    def startEvt = new Date(startTime)
    def endEvt = new Date(endTime)
   	log.debug "Days before is $state.daysBefore"
    log.debug "EventsBetween StartTime is $startEvt"
    log.debug "EventsBetween EndTime is $endEvt"	
    log.debug "We have $evts.length events in that time window"   
	def evts = switches.eventsBetween(startEvt, endEvt, [max: 1])
	evts.each{
		if(it.name == "switch"){
			if(it.value == "on"){
				switches.on()
			}
			else if(it.value == "off"){
				switches.off()
			}
		}
	}	
}

Cheers!


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy) #3

Thanks, Zach!

Great idea to use the event log… I didn’t think of that.

I’ve got some other techniques I’m exploring and will share later…


(Zach Naimon) #4

Actually recording events and storing those values in states seems like it won’t be scalable.

I’m interested to see what you’ve come up with.


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy) #5

I appreciate your comment, @speteor (Zach)…

Let’s analyze the processing complexity of the problem/solution in theory:

Note: Pardon the lack of mathematical simplicity in this verbose analysis. Assumptions are strewn throughout; alerts to mistakes or challenges of my assumptions or logic are very welcome…

If we run a SmartApp for “record activity” that does an explicit “subscribe()” to the on/off events of a list of many Things that the user selects as the “set of lights that represent home activity”; this is a linear demand (n x number of Things).

I am not sure how the SmartThings event handler is written; lets assume that after every event occurs (i.e., a switch is turned on), the event handler goes through various steps (send the physical command, acknowledge, send message to update the UI icon (I doubt the icon’s “poll” … unless you hit “refresh”), log the event, and then hash the registered event subscription table to see if any SmartApp methods need to be called.).

It is this last item that introduces the scaling problem: Lookup of subscriptions to self. As this table gets large, lookup time certainly will increase: However, if it is efficiently hashed or indexed, the increase in lookup time need not be exponential. From a global system-wide standpoint, we have to presume that the subscription table read/write functionality must be decently handled, because there are thousands (… millions) of SmartApps that are “watching” (subscribed to) 10x or 100x as many Things for events. In other words, adding a “record activity” SmartApp that subscribes to a list of Things will not, itself, increase the load on the subscription table – There are plenty of other uses for subscriptions in many other SmartApps that place an equally large load (e.g., monitoring a set of motion sensors or contact sensors).

Therefore, the only unusual load that this “record activity” SmartApp adds, is the work performed by the method called from the subscription. Again: A method call is not unusual load: Thousands of SmartApps have subscriptions which call methods that run some activity (e.g., contact sensor opens … calls a method within a SmartApp that turns on a light, sets off an alarm, sends a push notification, etc.).

The only unique action of the “record activity” SmartApp is to expand and write to the “state.*” object array (i.e., “persistent data storage”) of the SmartApp instance. One again, though, we can presume this must (or should) be handled efficiently by SmartThings… why? Because writing a piece of data upon an event call is “the same” as what the “event logging” function does; and since event logging is a fundamentally prevalent global activity, SmartThings would already have a major scalability issue if this was not scalable.

Consider the similar characteristics: Event entries belong to specific Things (aside… should I be using “Devices” or “Things” in this terminology, ick – hate using wrong jargon…). Therefore, the Event logs can be split across data stores easily. Searching the event log OF a particular Device is single threaded (does not seem to be any index besides date-time); but thousands of user’s device’s logs can be searched in parallel across multiple processors and data stores. Similarly, the “state.*” persistent store belongs to one and only one particular SmartApp instance; as far as I know, there is no way from one SmartApp to access the “state” of another SmartApp.

The difference: “state." is a base (and extensible( type; unlike "event.”. This means that the “state” table is more complex and has to be hashed to find specific attributes within state, and within arrays of attributes, etc…

Conclusion: Even if there is a magnitude of difference in the read/write requirements of the device event data vs. SmartApp state data, then the system remains scalable.

In other words: I absolutely admire and approve of your code (for this use case) which uses reading devices’s event logs by date range in order to find a list of prior activity. Definitely efficient, since there is no need to create additional subscriptions and history (state) data write workload. The event log is definitely optimized for WRITE activity, however, so there is some risk that READs were not efficiently implemented. Indeed: The code you provided uses a LINEAR scan of the log (within the date range) to search for specific types of events (switch.on; switch.off). There is no hash or index provided to efficiently extract these records.

Conversely: Having a SmartApp create it’s own event log for specific events on specific devices is additional WRITE activity, however it is limited to the specific event types we subscribe to (switch.on, switch.off, setLevel), and the “state” data structure is likely hashed, thus providing efficient READs.

Net Result: Theoretically, neither method is more or less scalable than the other. Hopefully this is true, because there are infinite ways that both subscribe() and state.* read/write are useful in situations where scanning the event table won’t suffice; unlike this particular SmartApp.

Rebuttals or discussions are encouraged!

Thanks,
…CP.


(Zach Naimon) #6

@CosmicPuppy -

I agree with your reasoning. Just a few things:

1- The SmartApps platform allows only 15 seconds for any code execution. This means that if running a particular function in your code takes more than 15 seconds, it gets timed out and doesn’t finish running.

2- Querying a possibly infinitely large table of state values for each switch is significantly less efficient than storing them in a proper database; something that the SmartApps platform doesn’t internally allow for.

Now, imagine a situation; someone lives in a large house with four-six family members. Each has their own room, and then there’s shared spaces (kitchen, dining room, living room, den, basement, etc.). Assuming this single family home is retrofitted entirely with SmartThings enabled switches, you’re looking at hundreds of events per day. Storing them in states is indeed possible, but querying from a table that large will certainly take time, and that time will eventually add up.

Now, a few things about my code:

There are only two possible event names for switches in the event log:

1- switch
2- null

The latter of which is the result of a SmartApp controlled switch change. Additionally, given the nature of my code, there is a maximum of ONE event returned every time the function is run due to “max: 1”, so essentially, all I’m doing is checking if the event name is ‘switch’ or ‘null,’ and if it’s the former, then check to see if the value is off or on. You could of course add functionality for a dimmer switch by simply doing something setting the dimmerVal to it.value.

Conclusion: My code creates a 1 event log every ten minutes, not really much read or write activity for that matter. Theoretically more efficient than storing every switch event in a state.


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy) #7

@speteor Yup… I definitely agree that your code is efficient and clever for this use case.

I just assert that there are unknown otheruse cases in which capturing events via individual subscriptions and writing to the state.* structure of a SmartApp will be “necessary” and thus:

  1. I hope the subscription table processing as well as the state.* data structure are reasonably optimized. The ability to hash search or index the state.* data structure might become essential for some situations. I presume SmartThings will keep watch for performance issues that could be addressed with language or method extensions.
  2. <li>I have experienced the 15 seconds limit in exploring some example code in the IDE (examples provided by SmartThings: probably buggy code and/or bugs in one of the object methods). Note that 15 seconds of actual CPU processing time is technically <em>humongous </em>(that's the technical term, I think) -- It would be very concerning to me as a developer if this time limit included explicit delayed execution (equivalent to sleep()); as well as any delays in response from Device or internet calls (which should be message queue interrupt driven, not polling or busy-waits)...</li>
    

    The latter item is easy to explore with some experimental SmartApp coding.

    Anyhow: Drifted off topic now, I guess; but appreciated the discussion.

    …CP.