├── LICENSE
├── README.md
├── devicetypes
└── jjhuff
│ └── rheem-econet-water-heater.src
│ └── rheem-econet-water-heater.groovy
└── smartapps
└── jjhuff
└── rheem-econet-connect.src
└── rheem-econet-connect.groovy
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SmartThings Rheem EcoNet
2 | ===============
3 |
4 | NOTE: this doesn't work anymore due to changes in Rheem's API.
5 |
6 | This is based on https://github.com/copy-ninja/SmartThings_RheemEcoNet with updates for the new API
7 |
8 | SmartThings installation instructions:
9 | --------------------------------------
10 | 1) Log in to your the SmartThings IDE. If you don't have a login yet, create one.
11 |
12 | 2) Load contents of [Smart App](smartapps/jjhuff/rheem-econet-connect.src/rheem-econet-connect.groovy) in SmartApps section. From IDE, navigate to My SmartApps > + New SmartApp > From Code. Click Save. Click Publish > "For Me"
13 |
14 | 3) Load contents of [Device Handler](devicetypes/jjhuff/rheem-econet-water-heater.src/rheem-econet-water-heater.groovy) in Device Handlers section. From IDE, navigate to My Device Handler > + New SmartDevice > From Code. Click Save. Click Publish "For Me"
15 |
16 | 4) In your mobile app, tap the "+", go to "My Apps", furnish your log in details and pick your gateway brand, and a list of devices will be available for you to pick
17 |
18 | Donations
19 | ---------
20 | If you like this project, please consider donating to one of these:
21 | * [EFF](https://www.eff.org/)
22 | * [Let's Encrypt](https://letsencrypt.org/)
23 |
--------------------------------------------------------------------------------
/devicetypes/jjhuff/rheem-econet-water-heater.src/rheem-econet-water-heater.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Rheem Econet Water Heater
3 | *
4 | * Copyright 2017 Justin Huff
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 | * in compliance with the License. You may obtain a copy of the License at:
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13 | * for the specific language governing permissions and limitations under the License.
14 | *
15 | * Last Updated : 2017-01-04
16 | *
17 | * Based on https://github.com/copy-ninja/SmartThings_RheemEcoNet
18 | */
19 | metadata {
20 | definition (name: "Rheem Econet Water Heater", namespace: "jjhuff", author: "Justin Huff") {
21 | capability "Actuator"
22 | capability "Refresh"
23 | capability "Sensor"
24 | capability "Switch"
25 | capability "Thermostat Heating Setpoint"
26 |
27 | command "heatLevelUp"
28 | command "heatLevelDown"
29 | command "updateDeviceData", ["string"]
30 | }
31 |
32 | simulator { }
33 |
34 | tiles {
35 | valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, width: 2, height: 2) {
36 | state("heatingSetpoint", label:'${currentValue}°',
37 | backgroundColors:[
38 | [value: 90, color: "#f49b88"],
39 | [value: 100, color: "#f28770"],
40 | [value: 110, color: "#f07358"],
41 | [value: 120, color: "#ee5f40"],
42 | [value: 130, color: "#ec4b28"],
43 | [value: 140, color: "#ea3811"]
44 | ]
45 | )
46 | }
47 | standardTile("heatLevelUp", "device.switch", canChangeIcon: false, decoration: "flat" ) {
48 | state("heatLevelUp", action:"heatLevelUp", icon:"st.thermostat.thermostat-up", backgroundColor:"#F7C4BA")
49 | }
50 | standardTile("heatLevelDown", "device.switch", canChangeIcon: false, decoration: "flat") {
51 | state("heatLevelDown", action:"heatLevelDown", icon:"st.thermostat.thermostat-down", backgroundColor:"#F7C4BA")
52 | }
53 |
54 | standardTile("switch", "device.switch", canChangeIcon: false, decoration: "flat" ) {
55 | state "on", label: 'On', action: "switch.off",
56 | icon: "st.switches.switch.on", backgroundColor: "#79b821"
57 | state("off", label: 'Off', action: "switch.on",
58 | icon: "st.switches.switch.off", backgroundColor: "#ffffff")
59 | }
60 |
61 | standardTile("refresh", "device.switch", decoration: "flat") {
62 | state("default", action:"refresh.refresh", icon:"st.secondary.refresh")
63 | }
64 |
65 | main "heatingSetpoint"
66 | details(["heatingSetpoint", "heatLevelUp", "heatLevelDown", "switch", "refresh"])
67 | }
68 | }
69 |
70 | def parse(String description) { }
71 |
72 | def refresh() {
73 | log.debug "refresh"
74 | parent.refresh()
75 | }
76 |
77 | def on() {
78 | parent.setDeviceEnabled(this.device, true)
79 | sendEvent(name: "switch", value: "off")
80 | }
81 |
82 | def off() {
83 | parent.setDeviceEnabled(this.device, false)
84 | sendEvent(name: "switch", value: "off")
85 | }
86 |
87 | def setHeatingSetpoint(Number setPoint) {
88 | /*heatingSetPoint = (heatingSetPoint < deviceData.minTemp)? deviceData.minTemp : heatingSetPoint
89 | heatingSetPoint = (heatingSetPoint > deviceData.maxTemp)? deviceData.maxTemp : heatingSetPoint
90 | */
91 | sendEvent(name: "heatingSetpoint", value: setPoint, unit: "F")
92 | parent.setDeviceSetPoint(this.device, setPoint)
93 | }
94 |
95 | def heatLevelUp() {
96 | def setPoint = device.currentValue("heatingSetpoint")
97 | setPoint = setPoint + 1
98 | setHeatingSetpoint(setPoint)
99 | }
100 |
101 | def heatLevelDown() {
102 | def setPoint = device.currentValue("heatingSetpoint")
103 | setPoint = setPoint - 1
104 | setHeatingSetpoint(setPoint)
105 | }
106 |
107 | def updateDeviceData(data) {
108 | sendEvent(name: "heatingSetpoint", value: data.setPoint, unit: "F")
109 | sendEvent(name: "switch", value: data.isEnabled ? "on" : "off")
110 | }
111 |
--------------------------------------------------------------------------------
/smartapps/jjhuff/rheem-econet-connect.src/rheem-econet-connect.groovy:
--------------------------------------------------------------------------------
1 | /**
2 | * Rheem EcoNet (Connect)
3 | *
4 | * Copyright 2017 Justin Huff
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7 | * in compliance with the License. You may obtain a copy of the License at:
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
13 | * for the specific language governing permissions and limitations under the License.
14 | *
15 | * Last Updated : 1/1/17
16 | *
17 | * Based on https://github.com/copy-ninja/SmartThings_RheemEcoNet
18 | *
19 | */
20 | definition(
21 | name: "Rheem EcoNet (Connect)",
22 | namespace: "jjhuff",
23 | author: "Justin Huff",
24 | description: "Connect to Rheem EcoNet",
25 | category: "SmartThings Labs",
26 | iconUrl: "http://smartthings.copyninja.net/icons/Rheem_EcoNet@1x.png",
27 | iconX2Url: "http://smartthings.copyninja.net/icons/Rheem_EcoNet@2x.png",
28 | iconX3Url: "http://smartthings.copyninja.net/icons/Rheem_EcoNet@3x.png")
29 |
30 |
31 | preferences {
32 | page(name: "prefLogIn", title: "Rheem EcoNet")
33 | page(name: "prefListDevice", title: "Rheem EcoNet")
34 | }
35 |
36 | /* Preferences */
37 | def prefLogIn() {
38 | def showUninstall = username != null && password != null
39 | return dynamicPage(name: "prefLogIn", title: "Connect to Rheem EcoNet", nextPage:"prefListDevice", uninstall:showUninstall, install: false) {
40 | section("Login Credentials"){
41 | input("username", "email", title: "Username", description: "Rheem EcoNet Email")
42 | input("password", "password", title: "Password", description: "Rheem EcoNet password (case sensitive)")
43 | }
44 | section("Advanced Options"){
45 | input(name: "polling", title: "Server Polling (in Minutes)", type: "int", description: "in minutes", defaultValue: "5" )
46 | }
47 | }
48 | }
49 |
50 | def prefListDevice() {
51 | if (login()) {
52 | def waterHeaterList = getWaterHeaterList()
53 | if (waterHeaterList) {
54 | return dynamicPage(name: "prefListDevice", title: "Devices", install:true, uninstall:true) {
55 | section("Select which water heater to use"){
56 | input(name: "waterheater", type: "enum", required:false, multiple:true, metadata:[values:waterHeaterList])
57 | }
58 | }
59 | } else {
60 | return dynamicPage(name: "prefListDevice", title: "Error!", install:false, uninstall:true) {
61 | section(""){ paragraph "Could not find any devices" }
62 | }
63 | }
64 | } else {
65 | return dynamicPage(name: "prefListDevice", title: "Error!", install:false, uninstall:true) {
66 | section(""){ paragraph "The username or password you entered is incorrect. Try again. " }
67 | }
68 | }
69 | }
70 |
71 |
72 | /* Initialization */
73 | def installed() { initialize() }
74 | def updated() {
75 | unsubscribe()
76 | initialize()
77 | }
78 | def uninstalled() {
79 | unschedule()
80 | unsubscribe()
81 | getAllChildDevices().each { deleteChildDevice(it) }
82 | }
83 |
84 | def initialize() {
85 | // Set initial states
86 | state.polling = [ last: 0, rescheduler: now() ]
87 |
88 | // Create selected devices
89 | def waterHeaterList = getWaterHeaterList()
90 | def selectedDevices = [] + getSelectedDevices("waterheater")
91 | selectedDevices.each {
92 | def dev = getChildDevice(it)
93 | def name = waterHeaterList[it]
94 | if (dev == null) {
95 | try {
96 | addChildDevice("jjhuff", "Rheem Econet Water Heater", it, null, ["name": "Rheem Econet: " + name])
97 | } catch (e) {
98 | log.debug "addChildDevice Error: $e"
99 | }
100 | }
101 | }
102 |
103 | // Remove unselected devices
104 | /*def deleteDevices = (selectedDevices) ? (getChildDevices().findAll { !selectedDevices.contains(it.deviceNetworkId) }) : getAllChildDevices()
105 | deleteDevices.each { deleteChildDevice(it.deviceNetworkId) } */
106 |
107 | //Subscribes to sunrise and sunset event to trigger refreshes
108 | subscribe(location, "sunrise", runRefresh)
109 | subscribe(location, "sunset", runRefresh)
110 | subscribe(location, "mode", runRefresh)
111 | subscribe(location, "sunriseTime", runRefresh)
112 | subscribe(location, "sunsetTime", runRefresh)
113 |
114 | //Refresh devices
115 | runRefresh()
116 | }
117 |
118 | def getSelectedDevices( settingsName ) {
119 | def selectedDevices = []
120 | (!settings.get(settingsName))?:((settings.get(settingsName)?.getAt(0)?.size() > 1) ? settings.get(settingsName)?.each { selectedDevices.add(it) } : selectedDevices.add(settings.get(settingsName)))
121 | return selectedDevices
122 | }
123 |
124 |
125 | /* Data Management */
126 | // Listing all the water heaters you have in Rheem EcoNet
127 | private getWaterHeaterList() {
128 | def deviceList = [:]
129 | apiGet("/locations", [] ) { response ->
130 | if (response.status == 200) {
131 | response.data.equipment[0].each {
132 | if (it.type.equals("Water Heater")) {
133 | deviceList["" + it.id]= it.name
134 | }
135 | }
136 | }
137 | }
138 | return deviceList
139 | }
140 |
141 | // Refresh data
142 | def refresh() {
143 | if (!login()) {
144 | return
145 | }
146 |
147 | log.info "Refreshing data..."
148 | // update last refresh
149 | state.polling?.last = now()
150 |
151 | // get all the children and send updates
152 | getAllChildDevices().each {
153 | def id = it.deviceNetworkId
154 | apiGet("/equipment/$id", [] ) { response ->
155 | if (response.status == 200) {
156 | log.debug "Got data: $response.data"
157 | it.updateDeviceData(response.data)
158 | }
159 | }
160 |
161 | }
162 |
163 | //schedule the rescheduler to schedule refresh ;)
164 | if ((state.polling?.rescheduler?:0) + 2400000 < now()) {
165 | log.info "Scheduling Auto Rescheduler.."
166 | runEvery30Minutes(runRefresh)
167 | state.polling?.rescheduler = now()
168 | }
169 | }
170 |
171 | // Schedule refresh
172 | def runRefresh(evt) {
173 | log.info "Last refresh was " + ((now() - state.polling?.last?:0)/60000) + " minutes ago"
174 | // Reschedule if didn't update for more than 5 minutes plus specified polling
175 | if ((((state.polling?.last?:0) + (((settings.polling?.toInteger()?:1>0)?:1) * 60000) + 300000) < now()) && canSchedule()) {
176 | log.info "Scheduling Auto Refresh.."
177 | schedule("* */" + ((settings.polling?.toInteger()?:1>0)?:1) + " * * * ?", refresh)
178 | }
179 |
180 | // Force Refresh NOWWW!!!!
181 | refresh()
182 |
183 | //Update rescheduler's last run
184 | if (!evt) state.polling?.rescheduler = now()
185 | }
186 |
187 | def setDeviceSetPoint(childDevice, setpoint) {
188 | log.info "setDeviceSetPoint: $childDevice.deviceNetworkId $setpoint"
189 | if (login()) {
190 | apiPut("/equipment/$childDevice.deviceNetworkId", [
191 | body: [
192 | setPoint: setpoint,
193 | ]
194 | ])
195 | }
196 |
197 | }
198 | def setDeviceEnabled(childDevice, enabled) {
199 | log.info "setDeviceEnabled: $childDevice.deviceNetworkId $enabled"
200 | if (login()) {
201 | apiPut("/equipment/$childDevice.deviceNetworkId", [
202 | body: [
203 | isEnabled: enabled,
204 | ]
205 | ])
206 | }
207 | }
208 |
209 | private login() {
210 | def apiParams = [
211 | uri: getApiURL(),
212 | path: "/auth/token",
213 | headers: ["Authorization": "Basic Y29tLnJoZWVtLmVjb25ldF9hcGk6c3RhYmxla2VybmVs"],
214 | requestContentType: "application/x-www-form-urlencoded",
215 | body: [
216 | username: settings.username,
217 | password: settings.password,
218 | "grant_type": "password"
219 | ],
220 | ]
221 | if (state.session?.expiration < now()) {
222 | try {
223 | httpPost(apiParams) { response ->
224 | if (response.status == 200) {
225 | log.debug "Login good!"
226 | state.session = [
227 | accessToken: response.data.access_token,
228 | refreshToken: response.data.refresh_token,
229 | expiration: now() + 150000
230 | ]
231 | return true
232 | } else {
233 | return false
234 | }
235 | }
236 | } catch (e) {
237 | log.debug "API Error: $e"
238 | return false
239 | }
240 | } else {
241 | // TODO: do a refresh
242 | return true
243 | }
244 | }
245 |
246 | /* API Management */
247 | // HTTP GET call
248 | private apiGet(apiPath, apiParams = [], callback = {}) {
249 | // set up parameters
250 | apiParams = [
251 | uri: getApiURL(),
252 | path: apiPath,
253 | headers: ["Authorization": getApiAuth()],
254 | requestContentType: "application/json",
255 | ] + apiParams
256 | log.debug "GET: $apiParams"
257 | try {
258 | httpGet(apiParams) { response ->
259 | callback(response)
260 | }
261 | } catch (e) {
262 | log.debug "API Error: $e"
263 | }
264 | }
265 |
266 | // HTTP PUT call
267 | private apiPut(apiPath, apiParams = [], callback = {}) {
268 | // set up parameters
269 | apiParams = [
270 | uri: getApiURL(),
271 | path: apiPath,
272 | headers: ["Authorization": getApiAuth()],
273 | requestContentType: "application/json",
274 | ] + apiParams
275 |
276 | try {
277 | httpPut(apiParams) { response ->
278 | callback(response)
279 | }
280 | } catch (e) {
281 | log.debug "API Error: $e"
282 | }
283 | }
284 |
285 | private getApiURL() {
286 | return "https://econet-api.rheemcert.com"
287 | }
288 |
289 | private getApiAuth() {
290 | return "Bearer " + state.session?.accessToken
291 | }
--------------------------------------------------------------------------------