/**
 *  Rollie Oil Tank Gauge
 *
 *  Version - 0.6
 *
 *  Copyright 2017 David LaPorte
 *
 *  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.
 *
 *  Instructions:
 *
 *	1) For US, visit: https://graph.api.smartthings.com
 *	2) For UK, visit: https://graph-eu01-euwest1.api.smartthings.com
 *	3) Click "My Device Handlers"
 *	4) Click "New Device Handler" in the top right
 *	5) Click the "From Code" tab
 *	6) Paste in the code from: https://github.com/dlaporte/SmartThings/blob/master/DeviceHandlers/rollie-gauge/rollie-gauge.groovy
 *	7) Click "Create"
 *	8) Click "Publish -> For Me"
 *
 */

include 'asynchttp_v1'

preferences {
	input(title: "", description: "Account Configuration", type: "paragraph", element: "paragraph")
	input(name: "serial", type: "text", title: "Gauge Serial Number", required: true, displayDuringSetup: true, capitalization: none)
	input(name: "password", type: "text", title: "Password",  required: true, defaultValue: "rollie", displayDuringSetup: true, capitalization: none)
    input(name: "pid", type: "text", title: "PID",  description: "search for \"id='pid'\" in the RollApp HTML", required: true, defaultValue: "0", displayDuringSetup: true, capitalization: none)
	input(title: "", description: "Alerting Configuration", type: "paragraph", element: "paragraph")
	input(name: "threshold", type: "number", title: "Low Oil Alert (gallons)", required: true, defaultValue: 15, capitalization: none)
	input(name: "email", type: "email", title: "Alert Email", required: true, displayDuringSetup: true, capitalization: none)
	input(name: "sms", type: "phone", title: "Alert SMS Number (10 digits)", required: true, displayDuringSetup: true, capitalization: none)
	input(title: "", description: "Tank Configuration", type: "paragraph", element: "paragraph")
	input(name: "tanktype", type: "enum", options: ["0": "275 Vertical", "1": "275 Horizontal", "2": "330 Vertical", "3": "330 Horizonal", "4": "Roth 1000L", "5": "Roth 1500L", "6": "Roth 400L", "7": "Vertical Cylinder", "8": "Horizontal Cylinder", "9": "Square Tank"], defaultValue: "0", title: "Tank Type", displayDuringSetup: true)
    
	input(title: "", description: "Vertical Cylinder Measurements (inches, optional) ", type: "paragraph image", image: "https://raw.githubusercontent.com/dlaporte/SmartThings/master/DeviceHandlers/rollie-gauge/images/vertical.png", element: "paragraph")
	input(name: "v_height", type: "number", title: "Height", required: false)
	input(name: "v_diameter", type: "number", title: "Diameter", required: false)

	input(title: "", description: "Horizontal Cylinder Measurements (inches, optional) ", type: "paragraph image", image: "https://raw.githubusercontent.com/dlaporte/SmartThings/master/DeviceHandlers/rollie-gauge/images/horizontal.png", element: "paragraph")
	input(name: "h_length", type: "number", title: "Length", required: false)
	input(name: "h_diameter", type: "number", title: "Diameter", required: false)

	input(title: "", description: "Square Tank Measurements (inches, optional) ", type: "paragraph image", image: "https://raw.githubusercontent.com/dlaporte/SmartThings/master/DeviceHandlers/rollie-gauge/images/square.png", element: "paragraph")
	input(name: "s_length", type: "number", title: "Length", required: false)
	input(name: "s_width", type: "number", title: "Width", required: false)
	input(name: "s_height", type: "number", title: "Height", required: false)
}

metadata {
    definition (name: "Rollie Oil Tank Gauge", namespace: "dlaporte", author: "David LaPorte") {
        capability "Configuration"
        capability "Polling"
        capability "Sensor"
		capability "Actuator"
        capability "Refresh"
        // kludge for ActionTiles tile support, "humidity"="percent remaining"
        capability "relativeHumidityMeasurement"

        attribute "gallons", "number"
        attribute "level", "number"
    }

    simulator {

    }

	tiles(scale: 2) {

		valueTile("gallons_icon", "device.gallons") {
			state("gallons", label: '${currentValue} gallons', unit: "gal",
				icon: "https://raw.githubusercontent.com/dlaporte/SmartThings/master/DeviceHandlers/rollie-gauge/images/oil-icon.png",
				backgroundColors: [
					[value: 0, color: "#bc2323"],
					[value: 50, color: "#1e9cbb"],
					[value: 100, color: "#7bb630"]
				]
			)
  		}

		multiAttributeTile(name:"summary", type:"generic", width:6, height:4) {
			tileAttribute("device.gallons", key: "PRIMARY_CONTROL") {
				attributeState("gallons", label: '${currentValue} gallons',
					icon: "https://raw.githubusercontent.com/dlaporte/SmartThings/master/DeviceHandlers/rollie-gauge/images/oil-drop-icon-png-large.png",
					unit: "gal",
					backgroundColors: [
						[value: 0, color: "#bc2323"],
						[value: 50, color: "#1e9cbb"],
						[value: 100, color: "#7bb630"]
					]
				)
			}

			tileAttribute("device.level", key: "SECONDARY_CONTROL") {
				attributeState("level", label: '${currentValue} inches')
	    		}
		}

		standardTile("today", "today", width: 2, height: 2) {
			state("default", label: "Today")
		}

		valueTile("gallons_today_usage", "device.gallons_today_usage", width: 2, height: 2, decoration: "flat", wordWrap: false) {
	        	state("gallons_today_usage", label: '${currentValue}')
		}

		valueTile("level_today_usage", "device.level_today_usage", width: 2, height: 2) {
			state("level_today_usage", label: '${currentValue}')
		}

		standardTile("yesterday", "yesterday", width: 2, height: 2) {
			state("default", label: "Yesterday")
		}

		valueTile("gallons_yesterday_usage", "device.gallons_yesterday_usage", width: 2, height: 2, decoration: "flat", wordWrap: false) {
			state("gallons_yesterday_usage", label: '${currentValue}')
		}

		valueTile("level_yesterday_usage", "device.level_yesterday_usage", width: 2, height: 2) {
	        	state("level_yesterday_usage", label: '${currentValue}')
		}

		standardTile("week", "week", width: 2, height: 2) {
			state("default", label: "Last Week")
		}

		valueTile("gallons_week_usage", "device.gallons_week_usage", width: 2, height: 2, decoration: "flat", wordWrap: false) {
			state("gallons_week_usage", label: '${currentValue}')
		}

		valueTile("level_week_usage", "device.level_week_usage", width: 2, height: 2) {
			state("level_week_usage", label: '${currentValue}')
		}

		standardTile("refresh", "device.poll", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state("default", action:"polling.poll", icon:"st.secondary.refresh")
		}

		standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
		    state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
		}

		valueTile("updated", "device.updated", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
		    state "updated", label: 'Updated:\n${currentValue}'
		}

		valueTile("level", "device.level", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "level", label: '${currentValue} inches'
		}

		main "gallons_icon"
  		details(["summary", "today", "gallons_today_usage", "level_today_usage", "yesterday", "gallons_yesterday_usage", "level_yesterday_usage", "week", "gallons_week_usage", "level_week_usage", "updated", "refresh", "configure"])
	}
}

def updated() {
	log.debug("updated with settings: ${settings.inspect()}")
	pullAllControllers()
    pullHistory()
}

def refresh() {
	log.debug "refresh called"
	pullAllControllers()
    pullHistory()
}

def poll() {
	log.debug "poll called"
	pullAllControllers()
	pullHistory()
}

def pullAllControllers() {
    log.debug "pullAllControllers called"

	rollieLogin()

	def params = [
		uri: "http://rollieapp.com",
        	path: "/gauges/AllControllers.php",
        	headers: [
			"Cookie": data.cookies
		]
	]

    try {
		asynchttp_v1.get('parseAllControllers', params)
    } catch (e) {
        log.error "something went wrong: $e"
    }
}

def parseAllControllers(response, data) {
	log.debug "parseAllControllers called"
	log.debug "Request was successful, ${response.status}"
	def m
	if ((m = response.getData() =~ /(?ms)<div class=\'container\'>(.+?)<\/div>/)) {
		String div = m[0][1]

		def xmlParser = new XmlSlurper()
		def table = xmlParser.parseText(div)

		def sn = table[0].children[1].children[0].children[15].children[0].text().trim()
		def date = table[0].children[1].children[0].children[15].children[1].text().trim()
		def time = table[0].children[1].children[0].children[15].children[2].text().trim()
		def level_fraction = table[0].children[1].children[0].children[15].children[3].text().trim()
		def gallons = table[0].children[1].children[0].children[15].children[4].text().trim()

        def percent = (int)((new BigDecimal(gallons) / new BigDecimal(state.tank_capacity)) * new BigDecimal(100))
    
		def whole = level_fraction.split('-')
        def fraction = whole[1].split('/')
		def level

		if (fraction[0] == "0" && fraction[1] == "0") {
        	level = new BigDecimal(whole[0])
        } else {
			level = new BigDecimal(whole[0]) + (new BigDecimal(fraction[0]) / new BigDecimal(fraction[1]))
    	}

        sendEvent(name: 'level', value: "${level}", unit: "inches")
		sendEvent(name: 'gallons', value: "${gallons}", unit: "gallons")
		sendEvent(name: 'date', value: "${date}")
		sendEvent(name: 'time', value: "${time}")
        sendEvent(name: 'sn', value: "${sn}", displayed: false)
		sendEvent(name: 'updated', value: "${date}\n${time}", displayed: false)

        sendEvent(name: 'humidity', value: "${percent}", unit: "%")

	} else {
		log.debug "parseAllControllers parse error"
	}
}

def pullHistory() {
    log.debug "pullHistory called"

	rollieLogin()

	def params = [
		uri: "http://rollieapp.com",
		path: "/gauges/loadhistory.php",
		query: ["sn": "${settings.serial}", "pid": "${settings.pid}"],
		headers: [
			"Cookie": data.cookies
		]
	]

    try {
		asynchttp_v1.get('parseHistory', params)
    } catch (e) {
        log.error "something went wrong: $e"
    }
}

def parseHistory(response, data) {
	log.debug "parseHistory called"
	log.debug "Request was successful, ${response.status}"

	def xmlParser = new XmlSlurper()
	def history = xmlParser.parseText(response.getData())


	def level_history = []
	def gallons_history = []
	def date_history = []

    history[0].children[1].children.each {
        def date = new Date().parse('yyyy-MM-dd hh:mm:ss', it.children[4].text() + " " + it.children[5].text())
        date_history.add(date)

        if (it.children[1].text()) {
            level_history.add(new BigDecimal(it.children[1].text()))
        } else {
            level_history.add(new BigDecimal(0))
        }
        if (it.children[2].text()) {
            gallons_history.add(new BigDecimal(it.children[2].text()))
        } else {
            gallons_history.add(new BigDecimal(0))
        }
    }

	def gallons_today_usage = gallons_history[-1] - device.currentValue("gallons")
	def level_today_usage = level_history[-1] - device.currentValue("level")

	if (gallons_history.size >= 1 && gallons_today_usage > 0 && level_today_usage > 0) {
		sendEvent(name: 'level_today_usage', value: "${level_today_usage}in", unit: "inches")
		sendEvent(name: 'gallons_today_usage', value: "${gallons_today_usage}gal", unit: "gallons")
	}

	def gallons_yesterday_usage = gallons_history[-2] - gallons_history[-1]
	def level_yesterday_usage = level_history[-2] - level_history[-1]

	if (gallons_history.size >= 2 && gallons_yesterday_usage > 0 && level_yesterday_usage > 0) {
		sendEvent(name: 'level_yesterday_usage', value: "${level_yesterday_usage}in", unit: "inches")
		sendEvent(name: 'gallons_yesterday_usage', value: "${gallons_yesterday_usage}gal", unit: "gallons")
	}

    if (gallons_history.size >= 8 && gallons_week_usage > 0 && level_week_usage > 0) {
    	def gallons_week_usage = gallons_history[-8] - gallons_history[-1]
   		def level_week_usage = level_history[-8] - level_history[-1]
    
		sendEvent(name: 'level_week_usage', value: "${level_week_usage}in", unit: "inches")
		sendEvent(name: 'gallons_week_usage', value: "${gallons_week_usage}gal", unit: "gallons")
	}
}

def rollieLogin() {
    log.debug "rollieLogin called"
    def params = [
        uri: "http://rollieapp.com",
        path: "/gauges"
    ]

    data.cookies = ''

	try {
        httpGet(params) { response ->
            log.debug "Request was successful, ${response.status}"
            response.getHeaders('Set-Cookie').each {
                String cookie = it.value.split(';')[0]
                log.debug "Adding cookie to collection: $cookie"
                data.cookies = data.cookies + cookie + ';'
            }
        }
    } catch (e) {
        log.error "something went wrong: $e"
    }

	params = [
        uri: "http://rollieapp.com",
        path: "/gauges/login.php",
        query: ["loginsn": "${settings.serial}", "password": "${settings.password}"],
        headers: [
          "Cookie": data.cookies
		]
	]
    try {
        httpGet(params) { response ->
            log.debug "Request was successful, ${response.status}"
            def doc = response.data
            log.debug "login status: ${doc.text()}"
		}
	} catch (e) {
    	log.error "something went wrong: $e"
	}
}

def configure() {
	log.debug "configure called"
    rollieLogin()
    
	def cylinder_capacity
    try {
		cylinder_capacity = v_height * (v_diameter/2) * (v_diameter/2) * 3.14 * 0.004329
    } catch (e) {
		cylinder_capacity = 0
    }
    
	def square_capacity
    try {
		square_capacity = s_length * s_width * s_height * 0.004329
    } catch (e) {
		square_capacity = 0
    }

	// capacity of tank (in gallons)
	def tank_capacities = [275,
    					   275,
                           330,
                           330,
                           1000,
                           1500,
                           400,
                           cylinder_capacity,
                           cylinder_capacity,
                           square_capacity,
                           square_capacity
                          ]
                          
	state.tank_capacity = tank_capacities[settings.tanktype.toInteger()]
    
    if (!settings.v_height) {
    	settings.v_height = "n"
	}
	if (!settings.v_diameter) {
    	settings.v_diameter = "n"
	}
	if (!settings.h_length) {
    	settings.h_length = "n"
	}
	if (!settings.h_diameter) {
    	settings.h_diameter = "n"
	}
	if (!settings.s_length) {
    	settings.s_length = "n"
	}
	if (!settings.s_width) {
    	settings.s_width = "n"
	}
	if (!settings.s_height) {
    	settings.s_height = "n"
	}
    
	def params = [
		uri: "http://rollieapp.com",
		path: "/gauges/updateclient.php",
		query: ["userdata[]": [
			"${settings.password}", "${settings.threshold}",
			"${settings.v_height}", "${settings.v_diameter}",
			"${settings.h_diameter}", "${settings.h_length}",
			"${settings.s_height}", "${settings.s_length}", "${settings.s_width}",
			"${settings.tanktype}", "1", "${settings.email}", "${settings.sms}", "${settings.pid}"]
		],
		headers: [
			"Cookie": data.cookies
		]
	]
	try {
        httpGet(params) { response ->
            log.debug "Request was successful, ${response.status}"
            def doc = response.data
            log.debug "configure status: ${doc.text()}"
        }
    } catch (e) {
        log.error "something went wrong: $e"
    }
}