Skip to content

Commit

Permalink
SmartApp development
Browse files Browse the repository at this point in the history
  • Loading branch information
BrettSheleski committed Oct 9, 2017
1 parent fca9d30 commit 3ce9919
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ metadata {

command "discover"
}

tiles(scale: 2) {


standardTile("discover", "discover", width: 2, height: 2) {
state "discover", label: 'Discover', action: "discover", icon: "st.secondary.refresh", backgroundColor: "#ffffff"
}


main "discover"
}
}

def parse(String description) {
Expand Down Expand Up @@ -31,7 +42,7 @@ def parse(String description) {

def discover(){
log.debug "DISCOVER"
return sendCommand("Status" , "0")
sendCommand("Status" , "0")
}


Expand All @@ -44,6 +55,8 @@ private def sendCommand(String command, String payload){
def hosthex = convertIPtoHex(ipAddress);
def porthex = convertPortToHex(port);

def deviceNetworkId = "$hosthex:$porthex"

def path = "/cm"

if (payload){
Expand All @@ -61,15 +74,10 @@ private def sendCommand(String command, String payload){
}
}

def result = new physicalgraph.device.HubAction(
method: "GET",
path: path,
headers: [
HOST: "${ipAddress}:${port}"
]
)
sendHubCommand(new physicalgraph.device.HubAction("""GET $path HTTP/1.1
HOST: ${ipAddress}:${port}
return result
""", physicalgraph.device.Protocol.LAN, "${deviceNetworkId}"))
}

private String convertIPtoHex(ipAddress) {
Expand Down
207 changes: 171 additions & 36 deletions smartapps/BrettSheleski/sonoff-tasmota.src/sonoff-tasmota.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,23 @@ definition(
)

preferences {
section("Sonoff Host") {
input "ipAddress", "string", title: "IP Address", required: true
}

section("Authentication") {
input(name: "username", type: "string", title: "Username", description: "Username", displayDuringSetup: false, required: false)
input(name: "password", type: "password", title: "Password", description: "Password", displayDuringSetup: false, required: false)
page("mainPage", "Main Configuration"){
section("Sonoff Host") {
input "ipAddress", "string", title: "IP Address", required: true
}

section("Authentication") {
input(name: "username", type: "string", title: "Username", description: "Username", displayDuringSetup: true, required: false)
input(name: "password", type: "password", title: "Password", description: "Password", displayDuringSetup: true, required: false)
}

section("Module"){
input(name: "moduleType", type: "enum", title: "Module Type", description: "Module Type", displayDuringSetup: true, required: false, refreshInterval: 5, options : getModuleTypesEnum())
}
}
}


def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
Expand All @@ -41,56 +48,184 @@ private removeChildDevices(delete) {
}

def initialize(){
sendCommand("Status", "0", "discoverCompleted")
}

def discoverCompleted(physicalgraph.device.HubResponse hubResponse){

log.debug "Discovery Completed"

def body = hubResponse.body;
def allSettings = [:];


if (body){
//log.debug "RESPONSE: ${body}";

def slurper = new groovy.json.JsonSlurper();

def settingsLine;


def lines = body.split('\n');

lines.each {
settingsLine = slurper.parseText(it.substring(it.indexOf('=') + 1));
allSettings << settingsLine;
}

def json = new groovy.json.JsonBuilder(allSettings).toPrettyString();
log.debug "SETTINGS : ${json}"

state.settings = allSettings;

setModuleTypeById(allSettings.Status.Module);

initializeCompleted(allSettings);
}
else{
log.debug "NO BODY!"
}
}

def setModuleTypeById(int id){

log.debug "Trying to set module for type ID : $id";

getModuleTypesMap().each{key, value ->
if (key == "$id"){
log.debug "Setting Module Type to ${value.name}"
settings.moduleType = value.name
}
}
}

def getModuleTypesEnum(){

def types = [];

getModuleTypesMap().each{key, value -> types << value.name}

return types;
}

def getModuleTypesMap(){
def deviceTypes = [:];

deviceTypes["1"] = ["name" : "Sonoff Basic"]

// SmartApps cannot create LAN requests
// Create a device that is specifically used to discover Sonoff device settings
deviceTypes["2"] = ["name" : "Sonoff RF"]

// get (or create) the discovery device
def discoveryDevice = getDiscoveryDevice()
deviceTypes["18"] = ["name" : "WeMos D1 mini"]

// tell the discovery device to make a
discoveryDevice.discover();
deviceTypes["25"] = ["name" : "Sonoff Bridge"]

return deviceTypes;
}

def discoverCompleted(Map deviceSettings){
log.debug "$deviceSettings";
deleteDiscoveryDevice();
def initializeCompleted(Map options = null){

def deviceTypesMap = getModuleTypesMap();

if (options == null)
{
options = state.settings;
}

if (options?.Status?.Module == 1){ // Sonoff Basic
initializeSonoffBasic(options);
}
else if (options?.Status?.Module == 20){ // Sonoff RF Bridge
initializeSonoffRfBridge(options);
}
}

def initializeSonoffBasic(Map options){

}

def getDiscoveryDevice(){
def initializeSonoffRfBridge(Map options){
def children = getChildDevices()
def namespace = app.namespace
def deviceName = "Sonoff-Tasmota Discovery Device"
def deviceName = "Sonoff-Tasmota RF Bridge Button"
def theHubId = location.hubs[0].id
def deviceId = "${app.id}-discoveryDevice"

def childDevice = getChildDevices().find {
it.deviceNetworkId == deviceId
}

for ( i in 1..16 )
{
def deviceId = "${app.id}-key${i}"
def childDevice = children.find {
it.deviceNetworkId == deviceId
}


if (!childDevice){
def deviceMap = [completedSetup: false]
if (childDevice){
log.debug "FOUND child device found for ${childDevice.deviceNetworkId}"
children.remove(childDevice)
}
else{
def deviceMap = [completedSetup: false]

deviceMap['name'] = app.label + " - Device Discovery";
deviceMap['label'] = deviceMap['name'];
deviceMap['name'] = app.label + " - Key $i";
deviceMap['label'] = deviceMap['name'];

childDevice = addChildDevice(namespace, deviceName, deviceId, theHubId, deviceMap)
}
childDevice = addChildDevice(namespace, deviceName, deviceId, theHubId, deviceMap)
}

childDevice.initChild([keyNumber : i]);

return childDevice
// remove any unused child Devices
removeChildDevices(children);
}
}

def deleteDiscoveryDevice(){
def deviceId = "${app.id}-discoveryDevice"
private def sendCommand(String command, String payload, String callback = null){

def childDevice = getChildDevices().find {
it.deviceNetworkId == deviceId
log.debug "sendCommand(${command}:${payload})"

def ipAddress = ipAddress;
def port = 80;
def hosthex = convertIPtoHex(ipAddress);
def porthex = convertPortToHex(port);

def dni = "$hosthex:$porthex";

def deviceNetworkId = "$hosthex:$porthex"

def path = "/cm"

def options = [callback : callback];
def headers = ["HOST": "$ipAddress:$port"]
def query = [:]

if (payload){
path += "?cmnd=${command}%20${payload}"
}
else{
path += "?cmnd=${command}"
}

if (childDevice)
{
deleteChildDevice(childDevice.deviceNetworkId)
if (username){
path += "&user=${username}"

if (password){
path += "&password=${password}"
}
}

def params = [path : path, method : "GET", protocol : physicalgraph.device.Protocol.LAN, headers : headers, query : query]

def hubAction = new physicalgraph.device.HubAction(params, dni, options)

sendHubCommand(hubAction)
}

private String convertIPtoHex(ipAddress) {
String hex = ipAddress.tokenize( '.' ).collect { String.format( '%02x', it.toInteger() ) }.join()
return hex
}

private String convertPortToHex(port) {
String hexport = port.toString().format('%04x', port.toInteger())
return hexport
}

0 comments on commit 3ce9919

Please sign in to comment.