Compare commits

..

1 Commits

Author SHA1 Message Date
Yuri b74af1c23e MSA-1631: Hi!
We want to introduce the web service smart app for SmartThings devices integration into our voice controlled ai bulter: https://play.google.com/store/apps/details?id=ai.cubic.home 
Please, provide your review. 

Regards Cubic Team
2016-12-02 05:56:39 -08:00
9 changed files with 382 additions and 63 deletions
@@ -39,7 +39,7 @@ metadata {
} }
def generatePresenceEvent(boolean present) { def generatePresenceEvent(boolean present) {
log.info "Life360 generatePresenceEvent($present)" log.debug "Here in generatePresenceEvent!"
def value = formatValue(present) def value = formatValue(present)
def linkText = getLinkText(device) def linkText = getLinkText(device)
def descriptionText = formatDescriptionText(linkText, present) def descriptionText = formatDescriptionText(linkText, present)
@@ -128,7 +128,7 @@ private Map parseCatchAllMessage(String description) {
if (cluster.command == 0x07) { if (cluster.command == 0x07) {
if (cluster.data[0] == 0x00){ if (cluster.data[0] == 0x00){
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]] sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
} }
else { else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}" log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
@@ -132,7 +132,7 @@ private Map parseCatchAllMessage(String description) {
if (cluster.command == 0x07) { if (cluster.command == 0x07) {
if (cluster.data[0] == 0x00) { if (cluster.data[0] == 0x00) {
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]] sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
} }
else { else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}" log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
@@ -161,7 +161,7 @@ private Map parseCatchAllMessage(String description) {
if (cluster.command == 0x07) { if (cluster.command == 0x07) {
if(cluster.data[0] == 0x00) { if(cluster.data[0] == 0x00) {
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]] sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
} }
else { else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}" log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
@@ -339,7 +339,7 @@ private Map getContactResult(value) {
log.debug "Contact: ${device.displayName} value = ${value}" log.debug "Contact: ${device.displayName} value = ${value}"
def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed' def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
sendEvent(name: 'contact', value: value, descriptionText: descriptionText, displayed: false, translatable: true) sendEvent(name: 'contact', value: value, descriptionText: descriptionText, displayed: false, translatable: true)
return [name: 'status', value: value, descriptionText: descriptionText, translatable: true] sendEvent(name: 'status', value: value, descriptionText: descriptionText, translatable: true)
} }
private getAccelerationResult(numValue) { private getAccelerationResult(numValue) {
@@ -119,7 +119,7 @@ private Map parseCatchAllMessage(String description) {
if (cluster.command == 0x07){ if (cluster.command == 0x07){
if (cluster.data[0] == 0x00) { if (cluster.data[0] == 0x00) {
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]] sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
} }
else { else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}" log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
@@ -103,7 +103,7 @@ private Map parseCatchAllMessage(String description) {
if (cluster.command == 0x07) { if (cluster.command == 0x07) {
if (cluster.data[0] == 0x00){ if (cluster.data[0] == 0x00){
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster
resultMap = [name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]] sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
} }
else { else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}" log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
@@ -16,15 +16,12 @@
* *
* Date: 2013-04-30 * Date: 2013-04-30
*/ */
// for the UI
metadata { metadata {
// Automatically generated. Make future change here.
definition (name: "SmartWeather Station Tile", namespace: "smartthings", author: "SmartThings") { definition (name: "SmartWeather Station Tile", namespace: "smartthings", author: "SmartThings") {
capability "Illuminance Measurement" capability "Illuminance Measurement"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Relative Humidity Measurement" capability "Relative Humidity Measurement"
capability "Sensor" capability "Sensor"
capability "Polling"
attribute "localSunrise", "string" attribute "localSunrise", "string"
attribute "localSunset", "string" attribute "localSunset", "string"
@@ -217,7 +214,7 @@ def poll() {
send(name: "localSunrise", value: localSunrise, descriptionText: "Sunrise today is at $localSunrise") send(name: "localSunrise", value: localSunrise, descriptionText: "Sunrise today is at $localSunrise")
send(name: "localSunset", value: localSunset, descriptionText: "Sunset today at is $localSunset") send(name: "localSunset", value: localSunset, descriptionText: "Sunset today at is $localSunset")
send(name: "illuminance", value: estimateLux(obs.solarradiation, sunriseDate, sunsetDate, weatherIcon) as Integer) send(name: "illuminance", value: estimateLux(sunriseDate, sunsetDate, weatherIcon))
// Forecast // Forecast
def f = get("forecast") def f = get("forecast")
@@ -308,12 +305,8 @@ private send(map) {
sendEvent(map) sendEvent(map)
} }
private estimateLux(solarradiation, sunriseDate, sunsetDate, weatherIcon) { private estimateLux(sunriseDate, sunsetDate, weatherIcon) {
def lux = 0 def lux = 0
if (solarradiation != '--') {
lux = solarradiation.toDouble() / 0.0079
} else {
def now = new Date().time def now = new Date().time
if (now > sunriseDate.time && now < sunsetDate.time) { if (now > sunriseDate.time && now < sunsetDate.time) {
//day //day
@@ -355,7 +348,6 @@ private estimateLux(solarradiation, sunriseDate, sunsetDate, weatherIcon) {
//could do calculations for dusk/dawn too //could do calculations for dusk/dawn too
lux = 10 lux = 10
} }
}
lux lux
} }
@@ -0,0 +1,327 @@
/**
* Cubic Home
* sdfssdfsfsdfsdfdsf
* Copyright 2016 Nikolay Zenovkin
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
definition(
name: "Cubic Butler for Smart Home",
namespace: "cubicrobotics",
author: "Cubic Robotics",
description: "Hey, Im Cubic, AI butler for smart home! \n" +
"I am the one and only app you need to control an entire home. \n" +
"Right now I can help you to control your SmartThings devices via natural speech and dashboard.\n" +
"Speak naturally, dont learn robot! My A.I. is designed for a smart home control.\n" +
"- You dont have to remember exact commands and phrases.\n" +
"- I have memory, and every new request will be processed in the context of previous ones.\n" +
"- I can ask clarifying questions, if Im not sure what you mean.",
category: "Convenience",
iconUrl: "https://lh3.googleusercontent.com/GX5BRhaFq22HpAEU6tD4JXvizlxWFuB9zjyZE39-pLpZvQvvUmVpWXa0v4-oaxz4tg=w300-rw",
iconX2Url: "https://lh3.googleusercontent.com/GX5BRhaFq22HpAEU6tD4JXvizlxWFuB9zjyZE39-pLpZvQvvUmVpWXa0v4-oaxz4tg=w300-rw",
iconX3Url: "https://lh3.googleusercontent.com/GX5BRhaFq22HpAEU6tD4JXvizlxWFuB9zjyZE39-pLpZvQvvUmVpWXa0v4-oaxz4tg=w300-rw",
oauth: [displayName: "Cubic Home", displayLink: "http://cubic.ai/"])
preferences {
section("Welcome to Cubic") {
// TODO: put inputs here
}
section("Allow Cubic to control these switches") {
input "switches", "capability.switch", multiple: true, required: false
}
section("Allow Cubic to control these motion sensors") {
input "motionSensors", "capability.motionSensor", multiple: true, required: false
}
section("Allow Cubic to control these bulbs") {
input "lamps", "capability.colorControl", multiple: true, required: false
}
section("Allow Cubic to control these thermostats") {
input "thermostats", "capability.thermostat", multiple: true, required: false
}
section("Allow Cubic to control these water sensors") {
input "waterSensors", "capability.waterSensor", multiple: true, required: false
}
section("Allow Cubic to control these CO sensors") {
input "smokeSensors", "capability.carbonMonoxideDetector", multiple: true, required: false
}
section("Allow Cubic to control these door controls") {
input "doorControls", "capability.doorControl", multiple: true, required: false
}
}
mappings {
path("/devices") {
action:
[
GET: "getDevices"
]
}
path("/devices/:id") {
action:
[
PUT: "updateDevice",
GET: "getDevice"
]
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
subscribe(motionSensors, "motion", motionHandler)
subscribe(switches, "switch", switchHandler)
subscribe(thermostats, "thermostat.temperature", thermostatHandler)
subscribe(waterSensors, "waterSensor", waterHandler)
subscribe(smokeSensors, "carbonMonoxideDetector", smokeHandler)
subscribe(doorControls, "doorControl", doorControlHandler)
}
def doorControlHandler(evt) {
if (evt.isStateChanged()) {
sendPushEvent(evt.deviceId,
"DOOR_CONTROL_SMART_THINGS",
evt.value
)
}
}
def smokeHandler(evt) {
if (evt.isStateChanged()) {
sendPushEvent(evt.deviceId,
"SMOKE_CO_SENSOR_SMART_THINGS",
evt.value
)
}
}
def thermostatHandler(evt) {
if (evt.isStateChanged()) {
sendPushEvent(evt.deviceId,
"THERMOSTAT_SMART_THINGS",
evt.value
)
}
}
def waterHandler(evt) {
if (evt.isStateChanged()) {
sendPushEvent(evt.deviceId,
"WATER_SENSOR_SMART_THINGS",
evt.value
)
}
}
def motionHandler(evt) {
if ("active" == evt.value) {
sendPushEvent(evt.deviceId,
"MOTION_SENSOR_SMART_THINGS",
evt.value)
}
}
def switchHandler(evt) {
log.debug "$evt.deviceId switch changed state to $evt.value"
}
def getDevices() {
def resp = []
switches.each {
resp << extractSwitch(it)
}
motionSensors.each {
resp << extractMotionSensor(it)
}
lamps.each {
resp << extractLamp(it)
}
thermostats.each {
resp << extractThermostat(it)
}
return resp
}
def getDevice() {
def id = params.id
log.info "Getting device by id " + id
def device = findDeviceById(id);
if (device == null) {
httpError(400, "$id is not a valid id for switch specified")
} else if (device.type == "SWITCH_SMART_THINGS") {
return extractSwitch(device.device);
} else if (device.type == "THERMOSTAT_SMART_THINGS") {
return extractThermostat(device.device);
} else if (device.type == "LAMP_SMART_THINGS") {
return extractLamp(device.device);
} else if (device.type == "MOTION_SENSOR_SMART_THINGS") {
return extractMotionSensor(device.device);
}
return nil
}
void updateDevice() {
def id = params.id
def device = findDeviceById(id)
if (device == null) {
httpError(400, "$id is not a valid switch id")
} else if (device.type == "SWITCH_SMART_THINGS") {
changeSwitchState(device, request.JSON?.powered)
} else if (device.type == "THERMOSTAT_SMART_THINGS") {
def heating_setpoint = request.JSON?.heating_setpoint
def cooling_setpoint = request.JSON?.cooling_setpoint
def thermostat_setpoint = request.JSON?.thermostat_setpoint
if (heating_setpoint != null) {
device.device.setHeatingSetpoint(heating_setpoint)
}
if (cooling_setpoint != null) {
device.device.setCoolingSetpoint(cooling_setpoint)
}
//if (thermostat_setpoint != null) {
// device.device.setThermostatSetpoint(thermostat_setpoint)
//}
} else if (device.type == "LAMP_SMART_THINGS") {
def powered = request.JSON?.powered
def color_h = request.JSON?.color_h
def color_s = request.JSON?.color_s
def color_b = request.JSON?.color_b
def color_temperature = request.JSON?.color_temperature
if (powered != null) {
changeSwitchState(device, request.JSON?.powered)
}
if (color_h != null) {
device.device.setHue(color_h)
}
if (color_b != null) {
device.device.setLevel(color_b)
}
if (color_s != null) {
device.device.setSaturation(color_s)
}
if (color_temperature != null) {
device.device.setColorTemperature(color_temperature)
}
} else if (device.type == "MOTION_SENSOR_SMART_THINGS") {
httpError(400, "Unable to control motion sensor")
}
}
def findDeviceById(id) {
def device = switches.find { it.id == id }
def type = "SWITCH_SMART_THINGS"
if (device == null) {
device = thermostats.find { it.id == id }
type = "THERMOSTAT_SMART_THINGS"
}
if (device == null) {
device = lamps.find { it.id == id }
type = "LAMP_SMART_THINGS"
}
if (device == null) {
device = motionSensors.find { it.id == id }
type = "MOTION_SENSOR_SMART_THINGS"
}
if (device == null) {
return null;
}
return [device: device, type: type];
}
def extractSwitch(it) {
return [id: it.id, type: "SWITCH_SMART_THINGS", state: [name: it.displayName, powered: it.currentValue("switch") == "on" ? true : false]]
}
def extractMotionSensor(it) {
return [id: it.id, type: "MOTION_SENSOR_SMART_THINGS", state: [name: it.displayName, state: it.currentValue("motion")]]
}
def extractLamp(it) {
return [id: it.id, type: "LAMP_SMART_THINGS", state: [name : it.displayName,
color_h : it.currentValue("hue"), color_s: it.currentValue("saturation"), color_b: it.currentValue("level"),
color_temperature: it.currentValue("colorTemperature"), powered: it.currentValue("switch") == "on" ? true : false]]
}
def extractThermostat(it) {
return [id: it.id, type: "THERMOSTAT_SMART_THINGS", state: [name : it.displayName,
temperature : it.currentValue("temperature"),
heating_setpoint : it.currentValue("heatingSetpoint"),
cooling_setpoint : it.currentValue("coolingSetpoint"),
thermostat_setpoint : it.currentValue("thermostatSetpoint"),
thermostat_mode : it.currentValue("thermostatMode"),
thermostat_fan_mode : it.currentValue("thermostatFanMode"),
thermostat_operating_state: it.currentValue("thermostatOperatingState")]]
}
def changeSwitchState(device, powered) {
log.info "Updating device " + device.device.displayName + " to state $powered"
switch (powered) {
case "true":
device.device.on()
break
case "false":
device.device.off()
break
default:
httpError(400, "$powered is not a valid power state for switch specified")
}
}
def sendPushEvent(pushEvent) {
def params = [
uri : "https://intent-processor-stage.cubic.ai/api/v1/pushEvent",
headers : [
Authorization: "Bearer -gm-IOuQR3W2Gim8Tjwsuw",
Accept : "/"
],
body : pushEvent,
requestContentType: "application/json"
]
try {
httpPost(params) { resp ->
log.debug "response data: ${resp.data}"
log.debug "response contentType: ${resp.contentType}"
}
} catch (e) {
log.debug "something went wrong: $e"
}
}
def sendPushEvent(id, type, state) {
sendPushEvent(
[device_id : id,
device_type: type,
state : state]
)
}
@@ -289,12 +289,12 @@ def initializeLife360Connection() {
state.life360AccessToken = result.data.access_token state.life360AccessToken = result.data.access_token
return true; return true;
} }
log.info "Life360 initializeLife360Connection, response=${result.data}" log.debug "Response=${result.data}"
return false; return false;
} }
catch (e) { catch (e) {
log.error "Life360 initializeLife360Connection, error: $e" log.debug e
return false; return false;
} }
@@ -656,7 +656,7 @@ def generateInitialEvent (member, childDevice) {
try { // we are going to just ignore any errors try { // we are going to just ignore any errors
log.info "Life360 generateInitialEvent($member, $childDevice)" log.debug "Generate Initial Event for New Device for Member = ${member.id}"
def place = state.places.find{it.id==settings.place} def place = state.places.find{it.id==settings.place}
@@ -678,8 +678,6 @@ def generateInitialEvent (member, childDevice) {
boolean isPresent = (distanceAway <= placeRadius) boolean isPresent = (distanceAway <= placeRadius)
log.info "Life360 generateInitialEvent, member: ($memberLatitude, $memberLongitude), place: ($placeLatitude, $placeLongitude), radius: $placeRadius, dist: $distanceAway, present: $isPresent"
// log.debug "External Id=${app.id}:${member.id}" // log.debug "External Id=${app.id}:${member.id}"
// def childDevice2 = getChildDevice("${app.id}.${member.id}") // def childDevice2 = getChildDevice("${app.id}.${member.id}")
@@ -720,7 +718,7 @@ def haversine(lat1, lon1, lat2, lon2) {
def placeEventHandler() { def placeEventHandler() {
log.info "Life360 placeEventHandler: params=$params, settings.place=$settings.place" log.debug "In placeEventHandler method."
// the POST to this end-point will look like: // the POST to this end-point will look like:
// POST http://test.com/webhook?circleId=XXXX&placeId=XXXX&userId=XXXX&direction=arrive // POST http://test.com/webhook?circleId=XXXX&placeId=XXXX&userId=XXXX&direction=arrive
@@ -731,6 +729,8 @@ def placeEventHandler() {
def direction = params?.direction def direction = params?.direction
def timestamp = params?.timestamp def timestamp = params?.timestamp
log.debug "Life360 Event: Circle: ${circleId}, Place: ${placeId}, User: ${userId}, Direction: ${direction}"
if (placeId == settings.place) { if (placeId == settings.place) {
def presenceState = (direction=="in") def presenceState = (direction=="in")
@@ -745,10 +745,10 @@ def placeEventHandler() {
if (deviceWrapper) { if (deviceWrapper) {
deviceWrapper.generatePresenceEvent(presenceState) deviceWrapper.generatePresenceEvent(presenceState)
log.debug "Life360 event raised on child device: ${externalId}" log.debug "Event raised on child device: ${externalId}"
} }
else { else {
log.warn "Life360 couldn't find child device associated with inbound Life360 event." log.debug "Couldn't find child device associated with inbound Life360 event."
} }
} }