emerald-hws 0.0.5__tar.gz → 0.0.7__tar.gz
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-0.0.5 → emerald_hws-0.0.7}/PKG-INFO +2 -2
- {emerald_hws-0.0.5 → emerald_hws-0.0.7}/pyproject.toml +2 -2
- {emerald_hws-0.0.5 → emerald_hws-0.0.7}/src/emerald_hws/emeraldhws.py +90 -67
- {emerald_hws-0.0.5 → emerald_hws-0.0.7}/src/emerald_hws.egg-info/PKG-INFO +2 -2
- emerald_hws-0.0.7/src/emerald_hws.egg-info/requires.txt +2 -0
- emerald_hws-0.0.5/src/emerald_hws.egg-info/requires.txt +0 -2
- {emerald_hws-0.0.5 → emerald_hws-0.0.7}/LICENSE +0 -0
- {emerald_hws-0.0.5 → emerald_hws-0.0.7}/README.md +0 -0
- {emerald_hws-0.0.5 → emerald_hws-0.0.7}/setup.cfg +0 -0
- {emerald_hws-0.0.5 → emerald_hws-0.0.7}/src/emerald_hws/__assets__/SFSRootCAG2.pem +0 -0
- {emerald_hws-0.0.5 → emerald_hws-0.0.7}/src/emerald_hws/__init__.py +0 -0
- {emerald_hws-0.0.5 → emerald_hws-0.0.7}/src/emerald_hws.egg-info/SOURCES.txt +0 -0
- {emerald_hws-0.0.5 → emerald_hws-0.0.7}/src/emerald_hws.egg-info/dependency_links.txt +0 -0
- {emerald_hws-0.0.5 → emerald_hws-0.0.7}/src/emerald_hws.egg-info/top_level.txt +0 -0
@@ -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
|
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "emerald_hws"
|
7
|
-
version = "0.0.
|
7
|
+
version = "0.0.7"
|
8
8
|
dependencies = [
|
9
9
|
"boto3",
|
10
|
-
"
|
10
|
+
"awsiotsdk"
|
11
11
|
]
|
12
12
|
authors = [
|
13
13
|
{ name="Ross Williamson", email="ross@inertia.net.nz" },
|
@@ -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():
|
@@ -31,7 +32,6 @@ class EmeraldHWS():
|
|
31
32
|
self.token = ""
|
32
33
|
self.properties = {}
|
33
34
|
self.logger = logging.getLogger()
|
34
|
-
self.logger.setLevel(logging.DEBUG)
|
35
35
|
|
36
36
|
def getLoginToken(self):
|
37
37
|
""" Performs an API request to get a token from the API
|
@@ -73,58 +73,46 @@ class EmeraldHWS():
|
|
73
73
|
post_response_json = post_response.json()
|
74
74
|
|
75
75
|
if post_response_json.get("code") == 200:
|
76
|
-
self.logger.debug("Successfully logged into Emerald API")
|
76
|
+
self.logger.debug("emeraldhws: Successfully logged into Emerald API")
|
77
77
|
self.properties = post_response_json.get("info").get("property")
|
78
78
|
else:
|
79
79
|
raise Exception("Unable to fetch properties from Emerald API")
|
80
80
|
|
81
|
-
def
|
82
|
-
"""
|
81
|
+
def connectMQTT(self):
|
82
|
+
""" Establishes a connection to Amazon IOT core's MQTT service
|
83
83
|
"""
|
84
84
|
|
85
|
+
cert_path = os.path.join(os.path.dirname(__file__), '__assets__', 'SFSRootCAG2.pem')
|
85
86
|
identityPoolID = self.COGNITO_IDENTITY_POOL_ID
|
86
87
|
region = self.MQTT_HOST.split('.')[2]
|
88
|
+
cognito_endpoint = "cognito-identity." + region + ".amazonaws.com"
|
87
89
|
cognitoIdentityClient = boto3.client('cognito-identity', region_name=region)
|
88
90
|
|
89
91
|
temporaryIdentityId = cognitoIdentityClient.get_id(IdentityPoolId=identityPoolID)
|
90
92
|
identityID = temporaryIdentityId["IdentityId"]
|
91
|
-
self.logger.debug("AWS IoT IdentityID: {}".format(identityID))
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
myAWSIoTMQTTClient.configureEndpoint(self.MQTT_HOST, 443)
|
115
|
-
myAWSIoTMQTTClient.configureCredentials(cert_path)
|
116
|
-
myAWSIoTMQTTClient.configureIAMCredentials(AccessKeyId, SecretKey, SessionToken)
|
117
|
-
myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
|
118
|
-
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1) # Infinite offline Publish queueing
|
119
|
-
myAWSIoTMQTTClient.configureDrainingFrequency(2) # Draining: 2 Hz
|
120
|
-
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10) # 10 sec
|
121
|
-
myAWSIoTMQTTClient.configureMQTTOperationTimeout(10) # 10 sec
|
122
|
-
myAWSIoTMQTTClient.onOffline = self.on_offline
|
123
|
-
myAWSIoTMQTTClient.onOnline = self.on_online
|
124
|
-
# Connect and subscribe to AWS IoT
|
125
|
-
myAWSIoTMQTTClient.connect()
|
126
|
-
|
127
|
-
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
|
128
116
|
|
129
117
|
def mqttDecodeUpdate(self, topic, payload):
|
130
118
|
""" Attempt to decode a received MQTT message and direct appropriately
|
@@ -140,28 +128,53 @@ class EmeraldHWS():
|
|
140
128
|
for key in json_payload[1]:
|
141
129
|
self.updateHWSState(hws_id, key, json_payload[1][key])
|
142
130
|
|
143
|
-
def mqttCallback(self,
|
131
|
+
def mqttCallback(self, publish_packet_data):
|
144
132
|
""" Calls decode update for received message
|
145
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)
|
146
138
|
|
147
|
-
|
148
|
-
|
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
|
149
154
|
|
150
|
-
def
|
151
|
-
"""
|
155
|
+
def on_lifecycle_connection_failure(self, lifecycle_connection_failure: mqtt5.LifecycleConnectFailureData):
|
156
|
+
""" Log message when connection failed
|
152
157
|
"""
|
158
|
+
self.logger.debug("emeraldhws: awsiot: connection failed")
|
159
|
+
return
|
153
160
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
160
172
|
|
161
|
-
def
|
162
|
-
"""
|
173
|
+
def on_lifecycle_attempting_connect(self, lifecycle_attempting_connect_data: mqtt5.LifecycleAttemptingConnectData):
|
174
|
+
""" Log message when attempting connect
|
163
175
|
"""
|
164
|
-
self.logger.debug("
|
176
|
+
self.logger.debug("emeraldhws: awsiot: attempting to connect")
|
177
|
+
return
|
165
178
|
|
166
179
|
def updateHWSState(self, id, key, value):
|
167
180
|
""" Updates the specified value for the supplied key in the HWS id specified
|
@@ -180,12 +193,17 @@ class EmeraldHWS():
|
|
180
193
|
""" Subscribes to the MQTT topics for the supplied HWS
|
181
194
|
:param id: The UUID of the requested HWS
|
182
195
|
"""
|
183
|
-
if not self.
|
196
|
+
if not self.mqttClient:
|
184
197
|
self.connectMQTT()
|
185
198
|
|
186
|
-
|
187
|
-
self.
|
188
|
-
|
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)
|
189
207
|
|
190
208
|
def getFullStatus(self, id):
|
191
209
|
""" Returns a dict with the full status of the specified HWS
|
@@ -222,42 +240,47 @@ class EmeraldHWS():
|
|
222
240
|
},
|
223
241
|
payload
|
224
242
|
]
|
225
|
-
|
226
|
-
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
|
227
250
|
|
228
251
|
def turnOn(self, id):
|
229
252
|
""" Turns the specified HWS on
|
230
253
|
:param id: The UUID of the HWS to turn on
|
231
254
|
"""
|
232
|
-
self.logger.debug("Sending control message: turn on")
|
255
|
+
self.logger.debug("emeraldhws: Sending control message: turn on")
|
233
256
|
self.sendControlMessage(id, {"switch":1})
|
234
257
|
|
235
258
|
def turnOff(self, id):
|
236
259
|
""" Turns the specified HWS off
|
237
260
|
:param id: The UUID of the HWS to turn off
|
238
261
|
"""
|
239
|
-
self.logger.debug("Sending control message: turn off")
|
262
|
+
self.logger.debug("emeraldhws: Sending control message: turn off")
|
240
263
|
self.sendControlMessage(id, {"switch":0})
|
241
264
|
|
242
265
|
def setNormalMode(self, id):
|
243
266
|
""" Sets the specified HWS to normal (not Boost or Quiet) mode
|
244
267
|
:param id: The UUID of the HWS to set to normal mode
|
245
268
|
"""
|
246
|
-
self.logger.debug("Sending control message: normal mode")
|
269
|
+
self.logger.debug("emeraldhws: Sending control message: normal mode")
|
247
270
|
self.sendControlMessage(id, {"mode":1})
|
248
271
|
|
249
272
|
def setBoostMode(self, id):
|
250
273
|
""" Sets the specified HWS to boost (high power) mode
|
251
274
|
:param id: The UUID of the HWS to set to boost mode
|
252
275
|
"""
|
253
|
-
self.logger.debug("Sending control message: boost mode")
|
276
|
+
self.logger.debug("emeraldhws: Sending control message: boost mode")
|
254
277
|
self.sendControlMessage(id, {"mode":0})
|
255
278
|
|
256
279
|
def setQuietMode(self, id):
|
257
280
|
""" Sets the specified HWS to quiet (low power) mode
|
258
281
|
:param id: The UUID of the HWS to set to quiet mode
|
259
282
|
"""
|
260
|
-
self.logger.debug("Sending control message: quiet mode")
|
283
|
+
self.logger.debug("emeraldhws: Sending control message: quiet mode")
|
261
284
|
self.sendControlMessage(id, {"mode":2})
|
262
285
|
|
263
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|