Skip to content

Commit

Permalink
Envoy 1.4 (20170309)/Solaredge 0.2 (20170309)
Browse files Browse the repository at this point in the history
• Add support for “Health Check” feature
• DTHs now refresh themselves without the need to employ an external
polling SmartApp (e.g., Pollster or CoRE)
• Fix format of Envoy installation date for display
  • Loading branch information
aahndee committed Mar 9, 2017
1 parent c2f369c commit aee0e5c
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 36 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,4 @@ The tiles below the chart show the total energy production for today, yesterday,
<img src="https://raw.githubusercontent.com/ahndee/Envoy-ST/master/devicetypes/aamann/enlighten-envoy-local.src/docs/IMG_2529.jpg" width="375px" height="667px" />

### Installation
Please see [this FAQ in the SmartThings Community](https://community.smartthings.com/t/faq-an-overview-of-using-custom-code-in-smartthings/16772) for instructions on how to install the device handler to your ST account.

As it is not currently possible for a DTH to refresh itself, the device needs to be polled regularly in order to get data continuously for the graph display. The easiest way to achieve this is to use [Pollster](https://community.smartthings.com/t/pollster-a-smartthings-polling-daemon/3447) and set it to refresh the device every 5 minutes (the Envoy updates its data once every 5 minutes). More advanced polling (e.g. polling during daylight hours only) can be achieved using other SmartApps such as [*CoRE*](https://community.smartthings.com/t/release-candidate-core-communitys-own-rule-engine/57972).
Please see [this FAQ in the SmartThings Community](https://community.smartthings.com/t/faq-an-overview-of-using-custom-code-in-smartthings/16772) for instructions on how to install the device handler to your ST account.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

def version() {
return "1.3.1 (20170108)\n© 2016–2017 Andreas Amann"
return "1.4 (20170309)\n© 2016–2017 Andreas Amann"
}

preferences {
Expand All @@ -40,6 +40,7 @@ metadata {
capability "Energy Meter"
capability "Refresh"
capability "Polling"
capability "Health Check"

attribute "energy_str", "string"
attribute "energy_yesterday", "string"
Expand Down Expand Up @@ -282,7 +283,24 @@ def updated() {
state.remove('api')
state.remove('installationDate')
state.maxPower = settings.confNumInverters * settings.confInverterSize
// Notify health check about this device with timeout interval 10 minutes (i.e., 5 failed update requests)
sendEvent(name: "checkInterval", value: 10 * 60, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
pullData()
startPoll()
}

def ping() {
log.trace("$device.displayName - checking device health…")
pullData()
}

def startPoll() {
unschedule()
// Schedule 2 minute polling
def sec = Math.round(Math.floor(Math.random() * 60))
def cron = "$sec 0/2 * * * ?" // every 2 min
log.trace("$device.displayName - startPoll: schedule('$cron', pullData)")
schedule(cron, pullData)
}

private def updateDNI() {
Expand Down Expand Up @@ -311,11 +329,11 @@ def pullData() {
state.lastRequestType = (state.api == "HTML" || !state.installationDate ? "HTML" : "JSON API")
log.debug "${device.displayName} - requesting latest data from Envoy via ${state.lastRequestType}"
updateDNI()
return new physicalgraph.device.HubAction([
sendHubCommand(new physicalgraph.device.HubAction([
method: "GET",
path: state.lastRequestType == "HTML" ? "/production?locale=en" : "/api/v1/production",
headers: [HOST:getHostAddress()]
])
]))
}

String getDataString(Integer seriesIndex) {
Expand Down Expand Up @@ -383,11 +401,11 @@ def parse(String message) {
msg.body.replaceFirst(/${patternString}/) {all, dateString ->
try {
state.installationDate = new Date().parse("E MMM dd, yyyy H:m a z", dateString).getTime()
log.debug "${device.displayName} - system has been live since ${dateString}"
sendEvent(name: 'installationDate', value: "System live since " + new Date(state.installationDate).format(), displayed: false)
log.debug "${device.displayName} - system has been live since ${dateString}"
sendEvent(name: 'installationDate', value: "System live since " + new Date(state.installationDate).format("MMM dd, yyyy"), displayed: false)
}
catch (Exception ex) {
log.debug "${device.displayName} - unable to parse installation date '${dateString}'"
log.debug "${device.displayName} - unable to parse installation date '${dateString}' ('${ex}')"
state.installationDate = -1
}
return true
Expand All @@ -414,6 +432,7 @@ def parse(String message) {
def data = state.api == "HTML" ? parseHTMLProductionData(msg.body) : msg.json
if (data == state.lastData) {
log.debug "${device.displayName} - no new data"
sendEvent(name: 'lastUpdate', value: new Date(), displayed: false) // dummy event for health check
return null
}
state.lastData = data
Expand Down
72 changes: 45 additions & 27 deletions devicetypes/aamann/solaredge.src/solaredge.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

def version() {
return "0.1.4 (20160715)\n© 2016 Andreas Amann"
return "0.2 (20170309)\n© 2016–2017 Andreas Amann"
}

preferences {
Expand All @@ -32,6 +32,7 @@ metadata {
capability "Energy Meter"
capability "Refresh"
capability "Polling"
capability "Health Check"

attribute "energy_str", "string"
attribute "energy_yesterday", "string"
Expand Down Expand Up @@ -283,7 +284,24 @@ def refresh() {
def updated() {
log.trace("$device.displayName updated with settings: ${settings.inspect()}")
device.setDeviceNetworkId(settings.confSiteID.toString())
// Notify health check about this device with timeout interval 20 minutes (i.e., 4 failed update requests)
sendEvent(name: "checkInterval", value: 20 * 60, data: [protocol: "cloud", hubHardwareId: device.hub.hardwareID], displayed: false)
pullData()
startPoll()
}

def ping() {
log.trace("$device.displayName - checking device health…")
pullData()
}

def startPoll() {
unschedule()
// Schedule 2 minute polling
def sec = Math.round(Math.floor(Math.random() * 60))
def cron = "$sec 0/5 * * * ?" // every 5 min
log.trace("$device.displayName - startPoll: schedule('$cron', pullData)")
schedule(cron, pullData)
}

def pullData() {
Expand Down Expand Up @@ -347,25 +365,25 @@ def pullData() {
def dataTable = []
def powerData = device.statesBetween("power", startOfToday - 1, startOfToday, [max: 288]) // 24h in 5min intervals should be more than sufficient…
if (powerData.size()) {
// work around a bug where the platform would return less than the requested number of events (as June 2016, only 50 events are returned)
while ((newValues = device.statesBetween("power", startOfToday - 1, powerData.last().date, [max: 288])).size()) {
powerData += newValues
}
powerData.reverse().each() {
dataTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.integerValue])
}
// work around a bug where the platform would return less than the requested number of events (as June 2016, only 50 events are returned)
while ((newValues = device.statesBetween("power", startOfToday - 1, powerData.last().date, [max: 288])).size()) {
powerData += newValues
}
powerData.reverse().each() {
dataTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.integerValue])
}
}
state.powerTableYesterday = dataTable
dataTable = []
def energyData = device.statesBetween("energy", startOfToday - 1, startOfToday, [max: 288])
if (energyData.size()) {
while ((newValues = device.statesBetween("energy", startOfToday - 1, energyData.last().date, [max: 288])).size()) {
energyData += newValues
}
// we drop the first point after midnight (0 energy) in order to have the graph scale correctly
energyData.reverse().drop(1).each() {
dataTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.floatValue])
}
while ((newValues = device.statesBetween("energy", startOfToday - 1, energyData.last().date, [max: 288])).size()) {
energyData += newValues
}
// we drop the first point after midnight (0 energy) in order to have the graph scale correctly
energyData.reverse().drop(1).each() {
dataTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.floatValue])
}
}
state.energyTableYesterday = dataTable
}
Expand All @@ -374,22 +392,22 @@ def pullData() {
powerTable = []
def powerData = device.statesSince("power", startOfToday, [max: 288])
if (powerData.size()) {
while ((newValues = device.statesBetween("power", startOfToday, powerData.last().date, [max: 288])).size()) {
powerData += newValues
}
powerData.reverse().each() {
powerTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.integerValue])
}
while ((newValues = device.statesBetween("power", startOfToday, powerData.last().date, [max: 288])).size()) {
powerData += newValues
}
powerData.reverse().each() {
powerTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.integerValue])
}
}
energyTable = []
def energyData = device.statesSince("energy", startOfToday, [max: 288])
if (energyData.size()) {
while ((newValues = device.statesBetween("energy", startOfToday, energyData.last().date, [max: 288])).size()) {
energyData += newValues
}
energyData.reverse().drop(1).each() {
energyTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.floatValue])
}
while ((newValues = device.statesBetween("energy", startOfToday, energyData.last().date, [max: 288])).size()) {
energyData += newValues
}
energyData.reverse().drop(1).each() {
energyTable.add([it.date.format("H", location.timeZone),it.date.format("m", location.timeZone),it.floatValue])
}
}
}
}
Expand Down

0 comments on commit aee0e5c

Please sign in to comment.