diff --git a/smartapps/cubicrobotics/cubic-butler-for-smart-home.src/cubic-butler-for-smart-home.groovy b/smartapps/cubicrobotics/cubic-butler-for-smart-home.src/cubic-butler-for-smart-home.groovy new file mode 100644 index 0000000..d7a7bce --- /dev/null +++ b/smartapps/cubicrobotics/cubic-butler-for-smart-home.src/cubic-butler-for-smart-home.groovy @@ -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, I’m 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, don’t learn robot! My A.I. is designed for a smart home control.\n" + + "- You don’t 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 I’m 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] + ) +} \ No newline at end of file