Welcome to Firefly’s documentation!¶
Contents:
Events¶
Standard Event Format¶
This is how event messages should be formatted:
{'dimmer':{'state':'on', 'level':'100'}}
This should follow the format of:
{'EVENT DEVICE TYPE': {'EVENT VAR': 'EVENT VAL'}}
Sending an Event¶
NOTE: In next release send_direct_event will be replaced with send_event
All files that require sending an event should follow the format:
from core.untils import send_direct_event
...
send_direct_event(<<DEVICE>>, <<EVENT IN STANDARD EVENT FORMAT>>)
Sample Event:
from core.utils import send_direct_event
...
send_direct_event('Kitchen Motion', {'motion':{'state':'active'}})
Plugins¶
- There are two different types of plugins:
- Device Plugin
- App Plugin
Plugins have a small set of required functions and libraries in order to function correcly. Besides these required functions there are no limits on what can be used with them. Below there are sample templates for the different plgin types.
NOTE: We do suggest using treq instead of requests to keep things running faster.
Difference between device plugin and app plugin:
- Device Plugin:
- Can reviece commands
- Can send events to the Firefly controller
- App Plugin:
- Can receive events. (ie triggers)
- Can subscribe to events from devices
Device Plugin¶
- There are two parts to the device plugins:
- The device its self
- The device installer script
Device installer scripts can be used to install more than just one device. i.e: SmartThings installer script installs various device types using a common smartthings libary. By doing this the installer script sets up a refresh loop to refresh all devices that it installed.
Device Template¶
NOTE: It is suggested to keep a ‘getValue’ function as shown in the sample device blow, But it is not required.
from core.models.device import Device
import logging
class Device(Device):
def __init__(self, deviceID, args={}):
self.METADATA = {
'title' : 'Plugin Title',
'type' : 'Plugin Type',
'package' : 'FOLDER NAME OF PLUGIN',
'module' : 'FILE NAME OF PLUGIN'
}
self.COMMANDS = {
'command' : self.function
}
self.REQUESTS = {
'request' : self.function
}
args = args.get('args')
name = args.get('name')
super(Device,self).__init__(deviceID, name)
# Your Code Goes Here
Sample Device¶
from core.models.device import Device
import logging
class Device(Device):
def __init__(self, deviceID, args={}):
self.METADATA = {
'title' : 'Firefly Presence Device',
'type' : 'presence',
'package' : 'ffPresence',
'module' : 'ffPresence'
}
self.COMMANDS = {
'presence' : self.setPresence
}
self.REQUESTS = {
'presence' : self.getPresence
}
args = args.get('args')
name = args.get('name')
super(Device,self).__init__(deviceID, name)
self._notify_present = args.get('notify_present')
self.notify_not_present = args.get('notify_not_present')
self.notify_device = args.get('notify_device')
self._presence = True
def setPresence(self, value):
from core.firefly_api import event_message
if value is not self.presence:
self.presence = value
event_message(self._name, "Setting Presence To " + str(value))
logging.debug("Setting Presence To " + str(value))
def getPresence(self):
return self.presence
@property
def presence(self):
return self._presence
@presence.setter
def presence(self, value):
self._presence = value
App Plugin¶
Location¶
The location module is used for:
- Location Based Event Scheduling
- Dawn Scheduling
- Sunrise Scheduling
- Solar Noon Scheduling
- Sunset Scheduling
- Dusk Scheduling
- Mode Handling
- Current Time
- Current Sun Status
- isDark
- isLight
Location Scheduler¶
Mode Handling¶
Time¶
Sun Status¶
Scheduler¶
There are two ways to use the scheduler:
- The Global Scheduler: Accesable from all modules - This is useful if you want one module to schedule and event and another module to unschdule the task
- Per Instance Scheduler: A scheduler object just used in one module.
Global Scheduler¶
Instance Scheduler¶
Scheduler Syntax¶
Config¶
Pushover Config¶
This goes into a file named pushover.json
{
"Zach Pushover": {
"api_key":"<<API KEY>>",
"user_key":"<<USER KEY>>",
"default_message":"This is a test message from firefly",
"default_priority":"high",
"presence_device":"Zach Presence"
},
"Ariel Pushover": {
"api_key":"<<API KEY>>",
"user_key":"<<USER KEY>>",
"default_message":"This is a test message from firefly",
"default_priority":"high",
"presence_device":"Ariel Presence"
}
}
SmartThings Config¶
This goes into a file name ZPSmartThings_settings.py
settings = {'access_token':'<TOKEN>', 'url':'<URL>'}
Make a SmartApp in the SmartThings IDE
//
// Definition
//
definition(
name: "Firefly Access",
namespace: "zpriddy",
author: "zpriddy",
description: "API access for Alexa.",
category: "Convenience",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
oauth: true) {
}
//
// Preferences
//
preferences {
section("Allow access to the following things...") {
input "contacts", "capability.contactSensor", title: "Which contact sensors?", multiple: true, required: false
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false
input "meters", "capability.powerMeter", title: "Which meters?", multiple: true, required: false
input "motions", "capability.motionSensor", title: "Which motion sensors?", multiple: true, required: false
input "presences", "capability.presenceSensor", title: "Which presence sensors?", multiple: true, required: false
input "dimmers", "capability.switchLevel", title: "Which dimmers?", multiple: true, required: false
input "switches", "capability.switch", title: "Which switches?", multiple: true, required: false
input "temperatures", "capability.temperatureMeasurement", title: "Which temperature sensors?", multiple: true, required: false
input "humidities", "capability.relativeHumidityMeasurement", title: "Which humidity sensors?", multiple: true, required: false
input "colors", "capability.colorControl", title: "Which Color Lights?", multiple: true, required: false
}
}
//
// Mappings
//
mappings {
path("/config") {
action: [
GET: "getConfig",
POST: "postConfig"
]
}
path("/contact") {
action: [
GET: "getContact"
]
}
path("/color") {
action: [
GET: "getColor",
POST: "postColor"
]
}
path("/devices") {
action: [
GET: "getDevices"
]
}
path("/lock") {
action: [
GET: "getLock",
POST: "postLock"
]
}
path("/mode") {
action: [
GET: "getMode",
POST: "postMode"
]
}
path("/motion") {
action: [
GET: "getMotion"
]
}
path("/phrase") {
action: [
GET: "getPhrase",
POST: "postPhrase"
]
}
path("/power") {
action: [
GET: "getPower"
]
}
path("/presence") {
action: [
GET: "getPresence"
]
}
path("/dimmer") {
action: [
GET: "getDimmer",
POST: "postDimmer"
]
}
path("/dimmerLevel") {
action: [
POST: "dimmerLevel"
]
}
path("/switch") {
action: [
GET: "getSwitch",
POST: "postSwitch"
]
}
path("/temperature") {
action: [
GET: "getTemperature"
]
}
path("/humidity") {
action: [
GET: "getHumidity"
]
}
path("/weather") {
action: [
GET: "getWeather"
]
}
}
//
// Installation
//
def installed() {
initialize()
}
def updated() {
unsubscribe()
initialize()
}
def initialize() {
state.dashingURI = ""
state.dashingAuthToken = ""
state.widgets = [
"contact": [:],
"lock": [:],
"mode": [:],
"motion": [:],
"power": [:],
"presence": [:],
"dimmer": [:],
"switch": [:],
"temperature": [:],
"humidity": [:],
"color" : [:],
]
subscribe(contacts, "contact", contactHandler)
subscribe(location, "mode", locationHandler)
subscribe(locks, "lock", lockHandler)
subscribe(motions, "motion", motionHandler)
subscribe(meters, "power", meterHandler)
subscribe(presences, "presence", presenceHandler)
subscribe(dimmers, "switch", dimmerSwitch)
subscribe(dimmers, "level", dimmerHandler)
subscribe(switches, "switch", switchHandler)
subscribe(temperatures, "temperature", temperatureHandler)
subscribe(humidities, "humidity", humidityHandler)
subscribe(colors, "color", colorHandler)
}
//
// Config
//
def getConfig() {
["dashingURI": state.dashingURI, "dashingAuthToken": state.dashingAuthToken]
}
def postConfig() {
state.dashingURI = request.JSON?.dashingURI
state.dashingAuthToken = request.JSON?.dashingAuthToken
respondWithSuccess()
}
//
// Contacts
//
def getContact() {
def deviceId = request.JSON?.deviceId
log.debug "getContact ${deviceId}"
if (deviceId) {
registerWidget("contact", deviceId, request.JSON?.widgetId)
def whichContact = contacts.find { it.displayName == deviceId }
if (!whichContact) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
return [
"deviceId": deviceId,
"deviceType":whichContact.name,
"state": whichContact.currentContact]
}
}
def result = [:]
contacts.each {
result[it.displayName] = [
"state": it.currentContact,
"deviceType":it.name,
"widgetId": state.widgets.contact[it.displayName]]}
return result
}
def contactHandler(evt) {
def widgetId = state.widgets.contact[evt.displayName]
notifyWidget(widgetId, ["state": evt.value])
}
//
// Locks
//
def getLock() {
def deviceId = request.JSON?.deviceId
log.debug "getLock ${deviceId}"
if (deviceId) {
registerWidget("lock", deviceId, request.JSON?.widgetId)
def whichLock = locks.find { it.displayName == deviceId }
if (!whichLock) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
return [
"deviceId": deviceId,
"deviceType":whichLock.name,
"state": whichLock.currentLock]
}
}
def result = [:]
locks.each {
result[it.displayName] = [
"state": it.currentLock,
"deviceType":it.name,
"widgetId": state.widgets.lock[it.displayName]]}
return result
}
def postLock() {
def command = request.JSON?.command
def deviceId = request.JSON?.deviceId
log.debug "postLock ${deviceId}, ${command}"
if (command && deviceId) {
def whichLock = locks.find { it.displayName == deviceId }
if (!whichLock) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
whichLock."$command"()
}
}
return respondWithSuccess()
}
def lockHandler(evt) {
def widgetId = state.widgets.lock[evt.displayName]
notifyWidget(widgetId, ["state": evt.value])
}
//
// Meters
//
def getPower() {
def deviceId = request.JSON?.deviceId
log.debug "getPower ${deviceId}"
if (deviceId) {
registerWidget("power", deviceId, request.JSON?.widgetId)
def whichMeter = meters.find { it.displayName == deviceId }
if (!whichMeter) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
return [
"deviceId": deviceId,
"value": whichMeter.currentValue("power"),
"deviceType":whichMeter.name,
"energy":whichMeter.currentValue("energy")]
}
}
def result = [:]
meters.each {
it.poll()
result[it.displayName] = [
"value": it.currentValue("power"),
"energy": it.currentValue("energy"),
"deviceType":it.name,
"widgetId": state.widgets.power[it.displayName]]}
return result
}
def meterHandler(evt) {
def widgetId = state.widgets.power[evt.displayName]
notifyWidget(widgetId, ["value": evt.value])
}
//
// Modes
//
def getMode() {
def result = [:]
def modeId = request.JSON?.modeId
def widgetId = request.JSON?.widgetId
if (widgetId) {
if (!state['widgets']['mode'].contains(widgetId)) {
state['widgets']['mode'].add(widgetId)
log.debug "registerWidget for mode: ${widgetId}"
}
}
if(modeId)
{
def whichMode = modes.find { it.displayName == modeId }
if (!whichMode) {
return respondWithStatus(404, "Device '${modeId}' not found.")
} else {
return [
"modeId": modeId ]
}
}
location.modes.each {
result[it.name] = [
"name": it.name]
}
return result
}
def postMode() {
def mode = request.JSON?.mode
log.debug "postMode ${mode}"
if (mode) {
setLocationMode(mode)
}
if (location.mode != mode) {
return respondWithStatus(404, "Mode not found.")
}
return respondWithSuccess()
}
def locationHandler(evt) {
for (i in state['widgets']['mode']) {
notifyWidget(i, ["mode": evt.value])
}
}
//
// Motions
//
def getMotion() {
def deviceId = request.JSON?.deviceId
log.debug "getMotion ${deviceId}"
if (deviceId) {
registerWidget("motion", deviceId, request.JSON?.widgetId)
def whichMotion = motions.find { it.displayName == deviceId }
if (!whichMotion) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
return [
"deviceId": deviceId,
"deviceType":whichMotion.name,
"state": whichMotion.currentValue("motion")]
}
}
def result = [:]
motions.each {
result[it.displayName] = [
"state": it.currentValue("motion"),
"deviceType":it.name,
"widgetId": state.widgets.motion[it.displayName]]}
return result
}
def motionHandler(evt) {
def widgetId = state.widgets.motion[evt.displayName]
notifyWidget(widgetId, ["state": evt.value])
}
//
// Phrases
//
def getPhrase() {
def result = [:]
// def phraseId = request.JSON?.phraseId
/*
location.helloHome.each {
result[it.name] = [
"name": it.name]
}
*/
result = location.helloHome?.getPhrases()*.label
log.debug result
log.debug "GETING PHRSES"
return result
}
def postPhrase() {
def phrase = request.JSON?.phrase
log.debug "postPhrase ${phrase}"
if (!phrase) {
respondWithStatus(404, "Phrase not specified.")
}
location.helloHome.execute(phrase)
return respondWithSuccess()
}
//
// Presences
//
def getPresence() {
def deviceId = request.JSON?.deviceId
log.debug "getPresence ${deviceId}"
if (deviceId) {
registerWidget("presence", deviceId, request.JSON?.widgetId)
def whichPresence = presences.find { it.displayName == deviceId }
if (!whichPresence) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
return [
"deviceId": deviceId,
"deviceType":whichPresence.name,
"state": whichPresence.currentPresence]
}
}
def result = [:]
presences.each {
result[it.displayName] = [
"state": it.currentPresence,
"deviceType":it.name,
"widgetId": state.widgets.presence[it.displayName]]}
return result
}
def presenceHandler(evt) {
def widgetId = state.widgets.presence[evt.displayName]
notifyWidget(widgetId, ["state": evt.value])
}
//
// Dimmers
//
def getDimmer() {
def deviceId = request.JSON?.deviceId
log.debug "getDimmer ${deviceId}"
if (deviceId) {
registerWidget("dimmer", deviceId, request.JSON?.widgetId)
def whichDimmer = dimmers.find { it.displayName == deviceId }
if (!whichDimmer) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
return [
"deviceId": deviceId,
"level": whichDimmer.currentValue("level"),
"deviceType":whichDimmer.name,
"state": whichDimmer.currentValue("switch")
]
}
}
def result = [:]
dimmers.each {
result[it.displayName] = [
"state": it.currentValue("switch"),
"level": it.currentValue("level"),
"deviceType":it.name,
"widgetId": state.widgets.dimmer[it.displayName]]}
return result
}
def postDimmer() {
def command = request.JSON?.command
def deviceId = request.JSON?.deviceId
log.debug "postDimmer ${deviceId}, ${command}"
if (command && deviceId) {
def whichDimmer = dimmers.find { it.displayName == deviceId }
if (!whichDimmer) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
whichDimmer."$command"()
}
}
return respondWithSuccess()
}
def dimmerLevel() {
def command = request.JSON?.command
def deviceId = request.JSON?.deviceId
log.debug "dimmerLevel ${deviceId}, ${command}"
command = command.toInteger()
if (command && deviceId) {
def whichDimmer = dimmers.find { it.displayName == deviceId }
if (!whichDimmer) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
whichDimmer.setLevel(command)
}
}
return respondWithSuccess()
}
def dimmerHandler(evt) {
def widgetId = state.widgets.dimmer[evt.displayName]
pause(1000)
notifyWidget(widgetId, ["level": evt.value])
}
def dimmerSwitch(evt) {
def whichDimmer = dimmers.find { it.displayName == evt.displayName }
def widgetId = state.widgets.dimmer[evt.displayName]
notifyWidget(widgetId, ["state": evt.value])
}
//
// Switches
//
def getSwitch() {
def deviceId = request.JSON?.deviceId
log.debug requestJSON
log.debug "getSwitch ${deviceId}"
if (deviceId) {
registerWidget("switch", deviceId, request.JSON?.widgetId)
def whichSwitch = switches.find { it.displayName == deviceId }
if (!whichSwitch) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
return [
"deviceId": deviceId,
"deviceType":whichSwitch.name,
"switch": whichSwitch.currentSwitch]
}
}
def result = [:]
switches.each {
result[it.displayName] = [
"state": it.currentSwitch,
"deviceType":it.name,
"widgetId": state.widgets.switch[it.displayName]]}
return result
}
def postSwitch() {
def command = request.JSON?.command
def deviceId = request.JSON?.deviceId
log.debug "postSwitch ${deviceId}, ${command}"
if (command && deviceId) {
def whichSwitch = switches.find { it.displayName == deviceId }
if (!whichSwitch) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
whichSwitch."$command"()
}
}
return respondWithSuccess()
}
def switchHandler(evt) {
def widgetId = state.widgets.switch[evt.displayName]
notifyWidget(widgetId, ["state": evt.value])
}
//
// Colors
//
def getColor() {
def deviceId = request.JSON?.deviceId
log.debug requestJSON
log.debug "getColor ${deviceId}"
if (deviceId) {
registerWidget("color", deviceId, request.JSON?.widgetId)
def whichColor = colors.find { it.displayName == deviceId }
if (!whichColor) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
return [
"deviceId": deviceId,
"deviceType":whichSwitch.name,
"color": whichColor.currentColor]
}
}
def result = [:]
colors.each {
result[it.displayName] = [
"hue": it.currentValue("hue"),
"sat": it.currentValue("saturation"),
"deviceType":it.name,
"widgetId": state.widgets.color[it.displayName]]}
return result
}
def postColor() {
def hex = request.JSON?.hex
def hue = request.JSON?.hue
def sat = request.JSON?.sat
def level = request.JSON?.level
def deviceId = request.JSON?.deviceId
log.debug "postColor ${deviceId}, ${hex}, ${hue}, ${sat}"
def value = [:]
value.hex = hex
value.saturation = (sat as Integer)/255*100
value.hue = (hue as Integer) /360*100
value.level = null
log.debug value
if (command && deviceId) {
def whichColor = colors.find { it.displayName == deviceId }
if (!whichColor) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
whichColor.setColor(value)
}
}
return respondWithSuccess()
}
def colorHandler(evt) {
def widgetId = state.widgets.color[evt.displayName]
notifyWidget(widgetId, ["color": evt.value])
}
//
// Temperatures
//
def getTemperature() {
def deviceId = request.JSON?.deviceId
log.debug "getTemperature ${deviceId}"
if (deviceId) {
registerWidget("temperature", deviceId, request.JSON?.widgetId)
def whichTemperature = temperatures.find { it.displayName == deviceId }
if (!whichTemperature) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
return [
"deviceId": deviceId,
"deviceType":whichTemperature.name,
"value": whichTemperature.currentTemperature]
}
}
def result = [:]
temperatures.each {
result[it.displayName] = [
"value": it.currentTemperature,
"deviceType":it.name,
"widgetId": state.widgets.temperature[it.displayName]]}
return result
}
def temperatureHandler(evt) {
def widgetId = state.widgets.temperature[evt.displayName]
notifyWidget(widgetId, ["value": evt.value])
}
//
// Humidities
//
def getHumidity() {
def deviceId = request.JSON?.deviceId
log.debug "getHumidity ${deviceId}"
if (deviceId) {
registerWidget("humidity", deviceId, request.JSON?.widgetId)
def whichHumidity = humidities.find { it.displayName == deviceId }
if (!whichHumidity) {
return respondWithStatus(404, "Device '${deviceId}' not found.")
} else {
return [
"deviceId": deviceId,
"deviceType":ehichHumidity.name,
"value": whichHumidity.currentHumidity]
}
}
def result = [:]
humidities.each {
result[it.displayName] = [
"value": it.currentHumidity,
"deviceType":it.name,
"widgetId": state.widgets.humidity[it.displayName]]}
return result
}
def humidityHandler(evt) {
def widgetId = state.widgets.humidity[evt.displayName]
notifyWidget(widgetId, ["value": evt.value])
}
//
// Weather
//
def getWeather() {
def feature = request.JSON?.feature
if (!feature) {
feature = "conditions"
}
return getWeatherFeature(feature)
}
//
// Get All Devices
//
def getDevices() {
def rDimmers = [:]
dimmers.each {
rDimmers[it.displayName] = [
"state": it.currentValue("switch"),
"level": it.currentValue("level"),
"deviceType":it.name]}
def rSwitches = [:]
switches.each {
rSwitches[it.displayName] = [
"state": it.currentSwitch,
"deviceType":it.name]}
def rMotion = [:]
motions.each {
rMotion[it.displayName] = [
"state": it.currentValue("motion"),
"deviceType":it.name]}
def rContacts = [:]
contacts.each {
rContacts[it.displayName] = [
"state": it.currentContact,
"deviceType":it.name]}
def result = [:]
result['contacts'] = rContacts
result['motion'] = rMotion
result['dimmers'] = rDimmers
result['switches'] = rSwitches
return result
}
//
// Widget Helpers
//
private registerWidget(deviceType, deviceId, widgetId) {
if (deviceType && deviceId && widgetId) {
state['widgets'][deviceType][deviceId] = widgetId
log.debug "registerWidget ${deviceType}:${deviceId}@${widgetId}"
}
}
private notifyWidget(widgetId, data) {
if (widgetId && state.dashingAuthToken) {
def uri = getWidgetURI(widgetId)
data["auth_token"] = state.dashingAuthToken
log.debug "notifyWidget ${uri} ${data}"
httpPostJson(uri, data)
}
}
private getWidgetURI(widgetId) {
state.dashingURI + "/widgets/${widgetId}"
}
//
// Response Helpers
//
private respondWithStatus(status, details = null) {
def response = ["error": status as Integer]
if (details) {
response["details"] = details as String
}
return response
}
private respondWithSuccess() {
return respondWithStatus(0)
}