emerald-hws 0.0.6__py3-none-any.whl → 0.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- emerald_hws/emeraldhws.py +90 -66
- {emerald_hws-0.0.6.dist-info → emerald_hws-0.0.7.dist-info}/METADATA +2 -2
- emerald_hws-0.0.7.dist-info/RECORD +8 -0
- emerald_hws-0.0.6.dist-info/RECORD +0 -8
- {emerald_hws-0.0.6.dist-info → emerald_hws-0.0.7.dist-info}/LICENSE +0 -0
- {emerald_hws-0.0.6.dist-info → emerald_hws-0.0.7.dist-info}/WHEEL +0 -0
- {emerald_hws-0.0.6.dist-info → emerald_hws-0.0.7.dist-info}/top_level.txt +0 -0
emerald_hws/emeraldhws.py
CHANGED
@@ -4,7 +4,8 @@ import os
|
|
4
4
|
import logging
|
5
5
|
import boto3
|
6
6
|
import random
|
7
|
-
from
|
7
|
+
from awsiot import mqtt5_client_builder, mqtt_connection_builder
|
8
|
+
from awscrt import mqtt5, http, auth, io
|
8
9
|
|
9
10
|
|
10
11
|
class EmeraldHWS():
|
@@ -72,58 +73,46 @@ class EmeraldHWS():
|
|
72
73
|
post_response_json = post_response.json()
|
73
74
|
|
74
75
|
if post_response_json.get("code") == 200:
|
75
|
-
self.logger.debug("Successfully logged into Emerald API")
|
76
|
+
self.logger.debug("emeraldhws: Successfully logged into Emerald API")
|
76
77
|
self.properties = post_response_json.get("info").get("property")
|
77
78
|
else:
|
78
79
|
raise Exception("Unable to fetch properties from Emerald API")
|
79
80
|
|
80
|
-
def
|
81
|
-
"""
|
81
|
+
def connectMQTT(self):
|
82
|
+
""" Establishes a connection to Amazon IOT core's MQTT service
|
82
83
|
"""
|
83
84
|
|
85
|
+
cert_path = os.path.join(os.path.dirname(__file__), '__assets__', 'SFSRootCAG2.pem')
|
84
86
|
identityPoolID = self.COGNITO_IDENTITY_POOL_ID
|
85
87
|
region = self.MQTT_HOST.split('.')[2]
|
88
|
+
cognito_endpoint = "cognito-identity." + region + ".amazonaws.com"
|
86
89
|
cognitoIdentityClient = boto3.client('cognito-identity', region_name=region)
|
87
90
|
|
88
91
|
temporaryIdentityId = cognitoIdentityClient.get_id(IdentityPoolId=identityPoolID)
|
89
92
|
identityID = temporaryIdentityId["IdentityId"]
|
90
|
-
self.logger.debug("AWS IoT IdentityID: {}".format(identityID))
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
myAWSIoTMQTTClient.configureEndpoint(self.MQTT_HOST, 443)
|
114
|
-
myAWSIoTMQTTClient.configureCredentials(cert_path)
|
115
|
-
myAWSIoTMQTTClient.configureIAMCredentials(AccessKeyId, SecretKey, SessionToken)
|
116
|
-
myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
|
117
|
-
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1) # Infinite offline Publish queueing
|
118
|
-
myAWSIoTMQTTClient.configureDrainingFrequency(2) # Draining: 2 Hz
|
119
|
-
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10) # 10 sec
|
120
|
-
myAWSIoTMQTTClient.configureMQTTOperationTimeout(10) # 10 sec
|
121
|
-
myAWSIoTMQTTClient.onOffline = self.on_offline
|
122
|
-
myAWSIoTMQTTClient.onOnline = self.on_online
|
123
|
-
# Connect and subscribe to AWS IoT
|
124
|
-
myAWSIoTMQTTClient.connect(keepAliveIntervalSecond=60)
|
125
|
-
|
126
|
-
self.myAWSIoTMQTTClient = myAWSIoTMQTTClient
|
93
|
+
self.logger.debug("emeraldhws: awsiot: AWS IoT IdentityID: {}".format(identityID))
|
94
|
+
|
95
|
+
credentials_provider = auth.AwsCredentialsProvider.new_cognito(
|
96
|
+
endpoint=cognito_endpoint,
|
97
|
+
identity=identityID,
|
98
|
+
tls_ctx=io.ClientTlsContext(io.TlsContextOptions()))
|
99
|
+
|
100
|
+
client = mqtt5_client_builder.websockets_with_default_aws_signing(
|
101
|
+
endpoint = self.MQTT_HOST,
|
102
|
+
region = region,
|
103
|
+
credentials_provider = credentials_provider,
|
104
|
+
on_connection_interrupted = self.on_connection_interrupted,
|
105
|
+
on_connection_resumed = self.on_connection_resumed,
|
106
|
+
on_lifecycle_connection_success = self.on_lifecycle_connection_success,
|
107
|
+
on_lifecycle_stopped = self.on_lifecycle_stopped,
|
108
|
+
on_lifecycle_attempting_connect = self.on_lifecycle_attempting_connect,
|
109
|
+
on_lifecycle_disconnection = self.on_lifecycle_disconnection,
|
110
|
+
on_lifecycle_connection_failure = self.on_lifecycle_connection_failure,
|
111
|
+
on_publish_received = self.mqttCallback
|
112
|
+
)
|
113
|
+
|
114
|
+
client.start()
|
115
|
+
self.mqttClient = client
|
127
116
|
|
128
117
|
def mqttDecodeUpdate(self, topic, payload):
|
129
118
|
""" Attempt to decode a received MQTT message and direct appropriately
|
@@ -139,28 +128,53 @@ class EmeraldHWS():
|
|
139
128
|
for key in json_payload[1]:
|
140
129
|
self.updateHWSState(hws_id, key, json_payload[1][key])
|
141
130
|
|
142
|
-
def mqttCallback(self,
|
131
|
+
def mqttCallback(self, publish_packet_data):
|
143
132
|
""" Calls decode update for received message
|
144
133
|
"""
|
134
|
+
publish_packet = publish_packet_data.publish_packet
|
135
|
+
assert isinstance(publish_packet, mqtt5.PublishPacket)
|
136
|
+
self.logger.debug("emeraldhws: awsiot: Received message from MQTT topic {}: {}".format(publish_packet.topic, publish_packet.payload))
|
137
|
+
self.mqttDecodeUpdate(publish_packet.topic, publish_packet.payload)
|
145
138
|
|
146
|
-
|
147
|
-
|
139
|
+
def on_connection_interrupted(self, connection, error, **kwargs):
|
140
|
+
""" Log error when MQTT is interrupted
|
141
|
+
"""
|
142
|
+
self.logger.debug("emeraldhws: awsiot: Connection interrupted. error: {}".format(error))
|
143
|
+
|
144
|
+
def on_connection_resumed(self, connection, return_code, session_present, **kwargs):
|
145
|
+
""" Log message when MQTT is resumed
|
146
|
+
"""
|
147
|
+
self.logger.debug("emeraldhws: awsiot: Connection resumed. return_code: {} session_present: {}".format(return_code, session_present))
|
148
|
+
|
149
|
+
def on_lifecycle_connection_success(self, lifecycle_connect_success_data: mqtt5.LifecycleConnectSuccessData):
|
150
|
+
""" Log message when connection succeeded
|
151
|
+
"""
|
152
|
+
self.logger.debug("emeraldhws: awsiot: connection succeeded")
|
153
|
+
return
|
148
154
|
|
149
|
-
def
|
150
|
-
"""
|
155
|
+
def on_lifecycle_connection_failure(self, lifecycle_connection_failure: mqtt5.LifecycleConnectFailureData):
|
156
|
+
""" Log message when connection failed
|
151
157
|
"""
|
158
|
+
self.logger.debug("emeraldhws: awsiot: connection failed")
|
159
|
+
return
|
152
160
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
161
|
+
def on_lifecycle_stopped(self, lifecycle_stopped_data: mqtt5.LifecycleStoppedData):
|
162
|
+
""" Log message when stopped
|
163
|
+
"""
|
164
|
+
self.logger.debug("emeraldhws: awsiot: stopped")
|
165
|
+
return
|
166
|
+
|
167
|
+
def on_lifecycle_disconnection(self, lifecycle_disconnect_data: mqtt5.LifecycleDisconnectData):
|
168
|
+
""" Log message when disconnected
|
169
|
+
"""
|
170
|
+
self.logger.debug("emeraldhws: awsiot: disconnected")
|
171
|
+
return
|
159
172
|
|
160
|
-
def
|
161
|
-
"""
|
173
|
+
def on_lifecycle_attempting_connect(self, lifecycle_attempting_connect_data: mqtt5.LifecycleAttemptingConnectData):
|
174
|
+
""" Log message when attempting connect
|
162
175
|
"""
|
163
|
-
self.logger.debug("
|
176
|
+
self.logger.debug("emeraldhws: awsiot: attempting to connect")
|
177
|
+
return
|
164
178
|
|
165
179
|
def updateHWSState(self, id, key, value):
|
166
180
|
""" Updates the specified value for the supplied key in the HWS id specified
|
@@ -179,12 +193,17 @@ class EmeraldHWS():
|
|
179
193
|
""" Subscribes to the MQTT topics for the supplied HWS
|
180
194
|
:param id: The UUID of the requested HWS
|
181
195
|
"""
|
182
|
-
if not self.
|
196
|
+
if not self.mqttClient:
|
183
197
|
self.connectMQTT()
|
184
198
|
|
185
|
-
|
186
|
-
self.
|
187
|
-
|
199
|
+
mqtt_topic = "ep/heat_pump/from_gw/{}".format(id)
|
200
|
+
subscribe_future = self.mqttClient.subscribe(
|
201
|
+
subscribe_packet=mqtt5.SubscribePacket(
|
202
|
+
subscriptions=[mqtt5.Subscription(
|
203
|
+
topic_filter=mqtt_topic,
|
204
|
+
qos=mqtt5.QoS.AT_LEAST_ONCE)]))
|
205
|
+
|
206
|
+
suback = subscribe_future.result(20)
|
188
207
|
|
189
208
|
def getFullStatus(self, id):
|
190
209
|
""" Returns a dict with the full status of the specified HWS
|
@@ -221,42 +240,47 @@ class EmeraldHWS():
|
|
221
240
|
},
|
222
241
|
payload
|
223
242
|
]
|
224
|
-
|
225
|
-
self.
|
243
|
+
mqtt_topic = "ep/heat_pump/to_gw/{}".format(id)
|
244
|
+
publish_future = self.mqttClient.publish(
|
245
|
+
mqtt5.PublishPacket(
|
246
|
+
topic=mqtt_topic,
|
247
|
+
payload=json.dumps(msg),
|
248
|
+
qos=mqtt5.QoS.AT_LEAST_ONCE))
|
249
|
+
publish_future.result(20) # 20 seconds
|
226
250
|
|
227
251
|
def turnOn(self, id):
|
228
252
|
""" Turns the specified HWS on
|
229
253
|
:param id: The UUID of the HWS to turn on
|
230
254
|
"""
|
231
|
-
self.logger.debug("Sending control message: turn on")
|
255
|
+
self.logger.debug("emeraldhws: Sending control message: turn on")
|
232
256
|
self.sendControlMessage(id, {"switch":1})
|
233
257
|
|
234
258
|
def turnOff(self, id):
|
235
259
|
""" Turns the specified HWS off
|
236
260
|
:param id: The UUID of the HWS to turn off
|
237
261
|
"""
|
238
|
-
self.logger.debug("Sending control message: turn off")
|
262
|
+
self.logger.debug("emeraldhws: Sending control message: turn off")
|
239
263
|
self.sendControlMessage(id, {"switch":0})
|
240
264
|
|
241
265
|
def setNormalMode(self, id):
|
242
266
|
""" Sets the specified HWS to normal (not Boost or Quiet) mode
|
243
267
|
:param id: The UUID of the HWS to set to normal mode
|
244
268
|
"""
|
245
|
-
self.logger.debug("Sending control message: normal mode")
|
269
|
+
self.logger.debug("emeraldhws: Sending control message: normal mode")
|
246
270
|
self.sendControlMessage(id, {"mode":1})
|
247
271
|
|
248
272
|
def setBoostMode(self, id):
|
249
273
|
""" Sets the specified HWS to boost (high power) mode
|
250
274
|
:param id: The UUID of the HWS to set to boost mode
|
251
275
|
"""
|
252
|
-
self.logger.debug("Sending control message: boost mode")
|
276
|
+
self.logger.debug("emeraldhws: Sending control message: boost mode")
|
253
277
|
self.sendControlMessage(id, {"mode":0})
|
254
278
|
|
255
279
|
def setQuietMode(self, id):
|
256
280
|
""" Sets the specified HWS to quiet (low power) mode
|
257
281
|
:param id: The UUID of the HWS to set to quiet mode
|
258
282
|
"""
|
259
|
-
self.logger.debug("Sending control message: quiet mode")
|
283
|
+
self.logger.debug("emeraldhws: Sending control message: quiet mode")
|
260
284
|
self.sendControlMessage(id, {"mode":2})
|
261
285
|
|
262
286
|
def isOn(self, id):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: emerald_hws
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.7
|
4
4
|
Summary: A package to manipulate and monitor Emerald Heat Pump Hot Water Systems
|
5
5
|
Author-email: Ross Williamson <ross@inertia.net.nz>
|
6
6
|
Project-URL: Homepage, https://github.com/ross-w/emerald_hws_py
|
@@ -12,7 +12,7 @@ Requires-Python: >=3.7
|
|
12
12
|
Description-Content-Type: text/markdown
|
13
13
|
License-File: LICENSE
|
14
14
|
Requires-Dist: boto3
|
15
|
-
Requires-Dist:
|
15
|
+
Requires-Dist: awsiotsdk
|
16
16
|
|
17
17
|
# emerald_hws_py
|
18
18
|
Python package for controlling Emerald Heat Pump Hot Water Systems
|
@@ -0,0 +1,8 @@
|
|
1
|
+
emerald_hws/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
2
|
+
emerald_hws/emeraldhws.py,sha256=0qbkwKatdNqf8UP6STo3mlIG-X5ZNuLNyqerQVuUgOQ,13251
|
3
|
+
emerald_hws/__assets__/SFSRootCAG2.pem,sha256=hw9W0AnYrrlbcWsOewAgIl1ULEsoO57Ylu35dCjWcS4,1424
|
4
|
+
emerald_hws-0.0.7.dist-info/LICENSE,sha256=zzMi56JX7OO-epbXNfe_oFW6sfZHSlO7Yxm0Oh0V014,1072
|
5
|
+
emerald_hws-0.0.7.dist-info/METADATA,sha256=UjewZVJkndoOV--3AIHOXPjwvnaDo35E0kHkLmi74hI,688
|
6
|
+
emerald_hws-0.0.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
7
|
+
emerald_hws-0.0.7.dist-info/top_level.txt,sha256=ZCiUmnBkDr2n4QVkTet1s_AKiGJjuz3heuCR5w5ZqLY,12
|
8
|
+
emerald_hws-0.0.7.dist-info/RECORD,,
|
@@ -1,8 +0,0 @@
|
|
1
|
-
emerald_hws/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
2
|
-
emerald_hws/emeraldhws.py,sha256=w7iSSo9hcsk8CoQsYD89Ar-PWyzVkjnpbxd8YEXrujc,12051
|
3
|
-
emerald_hws/__assets__/SFSRootCAG2.pem,sha256=hw9W0AnYrrlbcWsOewAgIl1ULEsoO57Ylu35dCjWcS4,1424
|
4
|
-
emerald_hws-0.0.6.dist-info/LICENSE,sha256=zzMi56JX7OO-epbXNfe_oFW6sfZHSlO7Yxm0Oh0V014,1072
|
5
|
-
emerald_hws-0.0.6.dist-info/METADATA,sha256=jHPA5zbpaLofBPi47Pkx0U7Jrpf9xTurvIAMuC-bh10,694
|
6
|
-
emerald_hws-0.0.6.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
7
|
-
emerald_hws-0.0.6.dist-info/top_level.txt,sha256=ZCiUmnBkDr2n4QVkTet1s_AKiGJjuz3heuCR5w5ZqLY,12
|
8
|
-
emerald_hws-0.0.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|