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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: emerald_hws
3
- Version: 0.0.5
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.5"
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():
@@ -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 getTemporaryCreds(self):
82
- """ Returns temporary credentials for IoT Core
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
- temporaryCredentials = cognitoIdentityClient.get_credentials_for_identity(IdentityId=identityID)
94
- self.logger.debug("Got new temporary credentials for AWS")
95
- self.identityID = identityID
96
- self.temporaryCredentials = temporaryCredentials
97
-
98
-
99
- def connectMQTT(self):
100
- """ Establishes a connection to Amazon IOT core's MQTT service
101
- """
102
-
103
- cert_path = os.path.join(os.path.dirname(__file__), '__assets__', 'SFSRootCAG2.pem')
104
- self.getTemporaryCreds()
105
-
106
- AccessKeyId = self.temporaryCredentials["Credentials"]["AccessKeyId"]
107
- SecretKey = self.temporaryCredentials["Credentials"]["SecretKey"]
108
- SessionToken = self.temporaryCredentials["Credentials"]["SessionToken"]
109
-
110
- # Init AWSIoTMQTTClient
111
- myAWSIoTMQTTClient = AWSIoTMQTTClient(self.identityID, useWebsocket=True)
112
-
113
- # AWSIoTMQTTClient configuration
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, client, userdata, message):
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
- self.logger.debug("Received message from MQTT topic {}: {}".format(message.topic,message.payload.decode("utf-8")))
148
- 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
149
154
 
150
- def on_offline(self):
151
- """ Reconfigures temporary credentials
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
- self.logger.debug("AWS IoT offline")
155
- self.getTemporaryCreds()
156
- AccessKeyId = self.temporaryCredentials["Credentials"]["AccessKeyId"]
157
- SecretKey = self.temporaryCredentials["Credentials"]["SecretKey"]
158
- SessionToken = self.temporaryCredentials["Credentials"]["SessionToken"]
159
- 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
160
172
 
161
- def on_online(self):
162
- """ Logs online state
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("AWS IoT online")
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.myAWSIoTMQTTClient:
196
+ if not self.mqttClient:
184
197
  self.connectMQTT()
185
198
 
186
- # self.myAWSIoTMQTTClient.subscribe("ep/heat_pump/to_gw/{}".format(id), 1, self.mqttCallback)
187
- self.myAWSIoTMQTTClient.subscribe("ep/heat_pump/from_gw/{}".format(id), 1, self.mqttCallback)
188
- # 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)
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.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
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.5
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