emerald-hws 0.0.6__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: emerald_hws
3
- Version: 0.0.6
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: AWSIoTPythonSDK
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.6"
7
+ version = "0.0.7"
8
8
  dependencies = [
9
9
  "boto3",
10
- "AWSIoTPythonSDK"
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 AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
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 getTemporaryCreds(self):
81
- """ Returns temporary credentials for IoT Core
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
- temporaryCredentials = cognitoIdentityClient.get_credentials_for_identity(IdentityId=identityID)
93
- self.logger.debug("Got new temporary credentials for AWS")
94
- self.identityID = identityID
95
- self.temporaryCredentials = temporaryCredentials
96
-
97
-
98
- def connectMQTT(self):
99
- """ Establishes a connection to Amazon IOT core's MQTT service
100
- """
101
-
102
- cert_path = os.path.join(os.path.dirname(__file__), '__assets__', 'SFSRootCAG2.pem')
103
- self.getTemporaryCreds()
104
-
105
- AccessKeyId = self.temporaryCredentials["Credentials"]["AccessKeyId"]
106
- SecretKey = self.temporaryCredentials["Credentials"]["SecretKey"]
107
- SessionToken = self.temporaryCredentials["Credentials"]["SessionToken"]
108
-
109
- # Init AWSIoTMQTTClient
110
- myAWSIoTMQTTClient = AWSIoTMQTTClient(self.identityID, useWebsocket=True)
111
-
112
- # AWSIoTMQTTClient configuration
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, client, userdata, message):
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
- self.logger.debug("Received message from MQTT topic {}: {}".format(message.topic,message.payload.decode("utf-8")))
147
- self.mqttDecodeUpdate(message.topic, message.payload)
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 on_offline(self):
150
- """ Reconfigures temporary credentials
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
- self.logger.debug("AWS IoT offline")
154
- self.getTemporaryCreds()
155
- AccessKeyId = self.temporaryCredentials["Credentials"]["AccessKeyId"]
156
- SecretKey = self.temporaryCredentials["Credentials"]["SecretKey"]
157
- SessionToken = self.temporaryCredentials["Credentials"]["SessionToken"]
158
- self.myAWSIoTMQTTClient.configureIAMCredentials(AccessKeyId, SecretKey, SessionToken)
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 on_online(self):
161
- """ Logs online state
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("AWS IoT online")
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.myAWSIoTMQTTClient:
196
+ if not self.mqttClient:
183
197
  self.connectMQTT()
184
198
 
185
- # self.myAWSIoTMQTTClient.subscribe("ep/heat_pump/to_gw/{}".format(id), 1, self.mqttCallback)
186
- self.myAWSIoTMQTTClient.subscribe("ep/heat_pump/from_gw/{}".format(id), 1, self.mqttCallback)
187
- # self.myAWSIoTMQTTClient.subscribe("ep/heat_pump/custom/topic/{}".format(id), 1, self.mqttCallback)
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.myAWSIoTMQTTClient.publish("ep/heat_pump/to_gw/{}".format(id), json.dumps(msg), 1)
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.6
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: AWSIoTPythonSDK
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,2 @@
1
+ boto3
2
+ awsiotsdk
@@ -1,2 +0,0 @@
1
- boto3
2
- AWSIoTPythonSDK
File without changes
File without changes
File without changes