From 608dd776fc432c72ddfc21fc392abc8a541e9a25 Mon Sep 17 00:00:00 2001 From: skysharma Date: Tue, 21 Feb 2023 08:40:17 -0600 Subject: [PATCH 01/18] 1. imports incl. google.api_core.datetime_helpers 2. Added logic to def from_json in class DeviceState and DeviceConfig to convert times to DateTimeWithNanoseconds and binaryData to bytes if env. vars set --- clearblade/cloud/iot_v1/device_types.py | 45 +++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/clearblade/cloud/iot_v1/device_types.py b/clearblade/cloud/iot_v1/device_types.py index e0667ed0..7c212540 100644 --- a/clearblade/cloud/iot_v1/device_types.py +++ b/clearblade/cloud/iot_v1/device_types.py @@ -1,7 +1,9 @@ from typing import List from .resources import GatewayType, LogLevel from .utils import get_value - +import os +from google.api_core.datetime_helpers import DatetimeWithNanoseconds +import base64 class Device(): """ @@ -132,8 +134,23 @@ def binary_data(self): @staticmethod def from_json(response_json): - return DeviceState(update_time=get_value(response_json, 'updateTime'), - binary_data=get_value(response_json, 'binaryData')) + updateTimeFromJson = get_value(response_json, 'updateTime') + binaryDataFromJson = get_value(response_json, 'binaryData') + + convert_times_to_datetime_with_nanoseconds = (False if os.environ.get("TIME_FORMAT") == None else os.environ.get("TIME_FORMAT").lower() == "datetimewithnanoseconds") + if convert_times_to_datetime_with_nanoseconds: + update_time = None if updateTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(updateTimeFromJson) + else: + update_time = updateTimeFromJson + + convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_FORMAT") == None else os.environ.get("BINARYDATA_FORMAT").lower() == "bytes") + if convert_binarydata_to_bytes: + binary_data = binaryDataFromJson.encode('utf-8') + else: + binary_data = binaryDataFromJson + + return DeviceState(update_time=update_time, + binary_data=binary_data) class Request(): @@ -220,11 +237,27 @@ def binary_data(self): @staticmethod def from_json(json): + cloudUpdateTimeFromJson = get_value(json,'cloudUpdateTime') + deviceAckTimeFromJson = get_value(json, 'deviceAckTime') + binaryDataFromJson = get_value(json,'binaryData') + + convert_times_to_datetime_with_nanoseconds = (False if os.environ.get("TIME_FORMAT") == None else os.environ.get("TIME_FORMAT").lower() == "datetimewithnanoseconds") + if convert_times_to_datetime_with_nanoseconds: + cloud_update_time = None if cloudUpdateTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(cloudUpdateTimeFromJson) + device_ack_time = None if deviceAckTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(deviceAckTimeFromJson) + else: + cloud_update_time = cloudUpdateTimeFromJson + device_ack_time = deviceAckTimeFromJson + + convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_FORMAT") == None else os.environ.get("BINARYDATA_FORMAT").lower() == "bytes") + if convert_binarydata_to_bytes: + binary_data = base64.b64decode(binaryDataFromJson.encode('utf-8')) + return DeviceConfig(name='', version=get_value(json, 'version'), - cloud_update_time=get_value(json,'cloudUpdateTime'), - device_ack_time=get_value(json, 'deviceAckTime'), - binary_data=get_value(json,'binaryData')) + cloud_update_time=cloud_update_time, + device_ack_time=device_ack_time, + binary_data=binary_data) class DeleteDeviceRequest(Request): From 5ab1fd336b220001636922d9c32f428067615db3 Mon Sep 17 00:00:00 2001 From: skysharma Date: Tue, 21 Feb 2023 13:07:25 -0600 Subject: [PATCH 02/18] 1. Changed Device.from_json to support GCP types for time, binaryData. 2. Changed DeviceState.from_json to handle blank binaryData 3. Changed DeviceConfig.from_json to handle blank binaryData --- clearblade/cloud/iot_v1/device_types.py | 83 ++++++++++++++++++++----- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/clearblade/cloud/iot_v1/device_types.py b/clearblade/cloud/iot_v1/device_types.py index 7c212540..ca824e67 100644 --- a/clearblade/cloud/iot_v1/device_types.py +++ b/clearblade/cloud/iot_v1/device_types.py @@ -39,14 +39,59 @@ def __init__(self, id: str, num_id: str = None, @staticmethod def from_json(json): - return Device(id=json['id'], num_id=json['numId'], - credentials=json['credentials'], last_heartbeat_time=json['lastHeartbeatTime'], - last_event_time=json['lastEventTime'], last_state_time=json['lastStateTime'], - last_config_ack_time=json['lastConfigAckTime'], last_config_send_time=json['lastConfigSendTime'], - blocked=json['blocked'], last_error_time=json['lastErrorTime'], - last_error_status_code=json['lastErrorStatus'], config=json['config'], - state=json['state'], log_level=json['logLevel'], meta_data=json['metadata'], - gateway_config=json['gatewayConfig']) + lastHeartbeatTimeFromJson = get_value(json,'lastHeartbeatTime') + lastEventTimeFromJson = get_value(json, 'lastEventTime') + lastStateTimeFromJson = get_value(json, 'lastStateTime') + lastConfigAckTimeFromJson = get_value(json, 'lastConfigAckTime') + lastConfigSendTimeFromJson = get_value(json, 'lastConfigSendTime') + lastErrorTimeFromJson = get_value(json, 'lastErrorTime') + + convert_times_to_datetime_with_nanoseconds = (False if os.environ.get("TIME_FORMAT") == None else os.environ.get("TIME_FORMAT").lower() == "datetimewithnanoseconds") + if convert_times_to_datetime_with_nanoseconds: + last_heartbeat_time = None if lastHeartbeatTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(lastHeartbeatTimeFromJson) + last_event_time = None if lastEventTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(lastEventTimeFromJson) + last_state_time = None if lastStateTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(lastStateTimeFromJson) + last_config_ack_time = None if lastConfigAckTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(lastConfigAckTimeFromJson) + last_config_send_time = None if lastConfigSendTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(lastConfigSendTimeFromJson) + last_error_time = None if lastErrorTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(lastErrorTimeFromJson) + else: + last_heartbeat_time = lastHeartbeatTimeFromJson + last_event_time = lastEventTimeFromJson + last_state_time = lastStateTimeFromJson + last_config_ack_time = lastConfigAckTimeFromJson + last_config_send_time = lastConfigSendTimeFromJson + last_error_time = lastErrorTimeFromJson + + convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_FORMAT") == None else os.environ.get("BINARYDATA_FORMAT").lower() == "bytes") + + deviceConfig = DeviceConfig.from_json(get_value(json, 'config')) + config = { "version": deviceConfig.version, "cloudUpdateTime": deviceConfig.cloud_update_time } + if (deviceConfig.binary_data not in [None, ""]): + config["binaryData"] = deviceConfig.binary_data + if (deviceConfig.device_ack_time not in [None, ""]): + config["deviceAckTime"] = deviceConfig.device_ack_time + + deviceState = DeviceState.from_json(get_value(json, 'state')) + state = { "updateTime": deviceState.update_time, "binaryData": deviceState.binary_data } + + return Device( + id=get_value(json, 'id'), + num_id=get_value(json, 'numId'), + credentials=get_value(json, 'credentials'), + last_heartbeat_time=last_heartbeat_time, + last_event_time=last_event_time, + last_state_time=last_state_time, + last_config_ack_time=last_config_ack_time, + last_config_send_time=last_config_send_time, + last_error_time=last_error_time, + blocked=get_value(json, 'blocked'), + last_error_status_code=get_value(json, 'lastErrorStatus'), + config=config, + state=state, + log_level=get_value(json, 'logLevel'), + meta_data=get_value(json, 'metadata'), + gateway_config=get_value(json, 'gatewayConfig') + ) @property def id(self): @@ -143,9 +188,12 @@ def from_json(response_json): else: update_time = updateTimeFromJson - convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_FORMAT") == None else os.environ.get("BINARYDATA_FORMAT").lower() == "bytes") - if convert_binarydata_to_bytes: - binary_data = binaryDataFromJson.encode('utf-8') + if (binaryDataFromJson not in [None, ""]): + convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_FORMAT") == None else os.environ.get("BINARYDATA_FORMAT").lower() == "bytes") + if convert_binarydata_to_bytes: + binary_data = binaryDataFromJson.encode('utf-8') + else: + binary_data = binaryDataFromJson else: binary_data = binaryDataFromJson @@ -249,10 +297,15 @@ def from_json(json): cloud_update_time = cloudUpdateTimeFromJson device_ack_time = deviceAckTimeFromJson - convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_FORMAT") == None else os.environ.get("BINARYDATA_FORMAT").lower() == "bytes") - if convert_binarydata_to_bytes: - binary_data = base64.b64decode(binaryDataFromJson.encode('utf-8')) - + if binaryDataFromJson not in [None, ""]: + convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_FORMAT") == None else os.environ.get("BINARYDATA_FORMAT").lower() == "bytes") + if convert_binarydata_to_bytes: + binary_data = base64.b64decode(binaryDataFromJson.encode('utf-8')) + else: + binary_data = binaryDataFromJson + else: + binary_data = binaryDataFromJson + return DeviceConfig(name='', version=get_value(json, 'version'), cloud_update_time=cloud_update_time, From a58eb3f2a423eea62bec5e27b50d9c74d088758e Mon Sep 17 00:00:00 2001 From: skysharma Date: Tue, 21 Feb 2023 13:08:07 -0600 Subject: [PATCH 03/18] Removed extra 'import base64' --- clearblade/cloud/iot_v1/devices.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/clearblade/cloud/iot_v1/devices.py b/clearblade/cloud/iot_v1/devices.py index 44fc9a9c..cc708468 100644 --- a/clearblade/cloud/iot_v1/devices.py +++ b/clearblade/cloud/iot_v1/devices.py @@ -1,5 +1,3 @@ -import base64 - from .config_manager import ClearBladeConfigManager from .device_types import * from .http_client import AsyncClient, SyncClient From d2bef223fd39479f05de5c7641da3076b4266598 Mon Sep 17 00:00:00 2001 From: skysharma Date: Tue, 21 Feb 2023 13:28:37 -0600 Subject: [PATCH 04/18] Added note about types of times, binaryData --- README.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.rst b/README.rst index 0480a6f8..9308e12b 100644 --- a/README.rst +++ b/README.rst @@ -78,3 +78,13 @@ Next Steps - and execute the setup.py file like , python setup.py install. - mostly if you change you imports from from google.cloud to clearblade.cloud everything else should work. + +Note about types of times and binaryData +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default time parameters (e.g. 'cloudUpdateTime', 'deviceAckTime', 'updateTime') are returned as RFC3339 strings (e.g. "2023-01-12T23:38:07.732Z"). +To return times formatted as 'DatetimeWithNanoseconds' (defined in the 'google.api_core.datetime_helpers' module) as per the Google IoTCore Python SDK, set environment variable TIME_FORMAT to exactly 'datetimewithnanoseconds'. +If this environment variable is not set, or is set to any other value, then the default format is returned. + +By default CONFIG binaryData is returned as a base64-encoded string and STATE binaryData is returned as a string (non-base64-encoded). +To return CONFIG and STATE binaryData as BYTE ARRAYS (non-base64-encoded) as per the Google IoTCore Python SDK, set environment variable BINARYDATA_FORMAT to exactly 'bytes'. If this environment variable is not set, or is set to any other value, then the default formats are returned. \ No newline at end of file From 61a2678073f1cf8a7c894283e248f1d2ee96d945 Mon Sep 17 00:00:00 2001 From: skysharma Date: Tue, 21 Feb 2023 13:34:52 -0600 Subject: [PATCH 05/18] Cleaned up formatting --- README.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 9308e12b..dd4d4963 100644 --- a/README.rst +++ b/README.rst @@ -82,9 +82,10 @@ Next Steps Note about types of times and binaryData ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -By default time parameters (e.g. 'cloudUpdateTime', 'deviceAckTime', 'updateTime') are returned as RFC3339 strings (e.g. "2023-01-12T23:38:07.732Z"). -To return times formatted as 'DatetimeWithNanoseconds' (defined in the 'google.api_core.datetime_helpers' module) as per the Google IoTCore Python SDK, set environment variable TIME_FORMAT to exactly 'datetimewithnanoseconds'. -If this environment variable is not set, or is set to any other value, then the default format is returned. +- By default time parameters (e.g. **cloudUpdateTime**, **deviceAckTime**, **updateTime**) are returned as **RFC3339** strings (e.g. "2023-01-12T23:38:07.732Z"). +- To return times formatted as **DatetimeWithNanoseconds** (defined in the **google.api_core.datetime_helpers** module) as returned by the **Google IoTCore Python SDK**, set environment variable **TIME_FORMAT** to exactly **datetimewithnanoseconds**. +- If this environment variable is not set, or is set to any other value, then the default format is returned. -By default CONFIG binaryData is returned as a base64-encoded string and STATE binaryData is returned as a string (non-base64-encoded). -To return CONFIG and STATE binaryData as BYTE ARRAYS (non-base64-encoded) as per the Google IoTCore Python SDK, set environment variable BINARYDATA_FORMAT to exactly 'bytes'. If this environment variable is not set, or is set to any other value, then the default formats are returned. \ No newline at end of file +- By default **CONFIG binaryData** is returned as a **base64-encoded string** and **STATE binaryData** is returned as a **NON-base64-encoded** string. +- To return CONFIG and STATE binaryData as **BYTE ARRAYS** as returned by the Google IoTCore Python SDK, set environment variable **BINARYDATA_FORMAT** to exactly **bytes**. +- If this environment variable is not set, or is set to any other value, then the default formats are returned. \ No newline at end of file From 2a76d3efda2cacd1a91d35fad45abdba285269ca Mon Sep 17 00:00:00 2001 From: skysharma Date: Tue, 21 Feb 2023 13:36:39 -0600 Subject: [PATCH 06/18] Further cleanup --- README.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.rst b/README.rst index dd4d4963..7ffac0c6 100644 --- a/README.rst +++ b/README.rst @@ -84,8 +84,6 @@ Note about types of times and binaryData - By default time parameters (e.g. **cloudUpdateTime**, **deviceAckTime**, **updateTime**) are returned as **RFC3339** strings (e.g. "2023-01-12T23:38:07.732Z"). - To return times formatted as **DatetimeWithNanoseconds** (defined in the **google.api_core.datetime_helpers** module) as returned by the **Google IoTCore Python SDK**, set environment variable **TIME_FORMAT** to exactly **datetimewithnanoseconds**. -- If this environment variable is not set, or is set to any other value, then the default format is returned. - - By default **CONFIG binaryData** is returned as a **base64-encoded string** and **STATE binaryData** is returned as a **NON-base64-encoded** string. - To return CONFIG and STATE binaryData as **BYTE ARRAYS** as returned by the Google IoTCore Python SDK, set environment variable **BINARYDATA_FORMAT** to exactly **bytes**. -- If this environment variable is not set, or is set to any other value, then the default formats are returned. \ No newline at end of file +- If these environment variables are not set, or are set to any unexpeced values, then the default formats are returned. \ No newline at end of file From 5c8c277922820cd2bc1866059871bce1d5aeeb6a Mon Sep 17 00:00:00 2001 From: skysharma Date: Tue, 21 Feb 2023 14:15:47 -0600 Subject: [PATCH 07/18] Added copyright info. at top of files --- README.rst | 21 ++++++++++++++++++ clearblade/cloud/iot_v1/device_types.py | 29 +++++++++++++++++++++++++ clearblade/cloud/iot_v1/devices.py | 29 +++++++++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/README.rst b/README.rst index 7ffac0c6..cdcb2ef9 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,24 @@ +.. Copyright 2023 ClearBlade Inc. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + Copyright 2022 Google LLC + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + Python Client for ClearBlade Internet of Things (IoT) Core API ================================================================ diff --git a/clearblade/cloud/iot_v1/device_types.py b/clearblade/cloud/iot_v1/device_types.py index ca824e67..2f2dd9de 100644 --- a/clearblade/cloud/iot_v1/device_types.py +++ b/clearblade/cloud/iot_v1/device_types.py @@ -1,3 +1,32 @@ +# -*- coding: utf-8 -*- +# Copyright 2023 ClearBlade Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# from typing import List from .resources import GatewayType, LogLevel from .utils import get_value diff --git a/clearblade/cloud/iot_v1/devices.py b/clearblade/cloud/iot_v1/devices.py index cc708468..42ebbe4a 100644 --- a/clearblade/cloud/iot_v1/devices.py +++ b/clearblade/cloud/iot_v1/devices.py @@ -1,3 +1,32 @@ +# -*- coding: utf-8 -*- +# Copyright 2023 ClearBlade Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# from .config_manager import ClearBladeConfigManager from .device_types import * from .http_client import AsyncClient, SyncClient From 51d72001acbb05047ff0874b1f8054b5c12e1a03 Mon Sep 17 00:00:00 2001 From: skysharma Date: Wed, 22 Feb 2023 16:03:21 -0600 Subject: [PATCH 08/18] Not to 'pip install google-api-core' --- README.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index cdcb2ef9..6290bc10 100644 --- a/README.rst +++ b/README.rst @@ -104,7 +104,10 @@ Note about types of times and binaryData ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - By default time parameters (e.g. **cloudUpdateTime**, **deviceAckTime**, **updateTime**) are returned as **RFC3339** strings (e.g. "2023-01-12T23:38:07.732Z"). -- To return times formatted as **DatetimeWithNanoseconds** (defined in the **google.api_core.datetime_helpers** module) as returned by the **Google IoTCore Python SDK**, set environment variable **TIME_FORMAT** to exactly **datetimewithnanoseconds**. +- To return times formatted as **DatetimeWithNanoseconds** (defined in the **google.api_core.datetime_helpers** module) as returned by the **Google IoTCore Python SDK**: +1. Run **pip install google-api-core** to install appropriate type. +2. Set environment variable **TIME_FORMAT** to exactly **datetimewithnanoseconds**. + - By default **CONFIG binaryData** is returned as a **base64-encoded string** and **STATE binaryData** is returned as a **NON-base64-encoded** string. - To return CONFIG and STATE binaryData as **BYTE ARRAYS** as returned by the Google IoTCore Python SDK, set environment variable **BINARYDATA_FORMAT** to exactly **bytes**. - If these environment variables are not set, or are set to any unexpeced values, then the default formats are returned. \ No newline at end of file From 7b43236172b2100e79991a8dab8d6068546cedd8 Mon Sep 17 00:00:00 2001 From: skysharma Date: Wed, 22 Feb 2023 17:44:44 -0600 Subject: [PATCH 09/18] Removed instructions for installing datetime_helpers module. Also now changed 'google.api_core' to 'proto' --- README.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 6290bc10..1e3d5713 100644 --- a/README.rst +++ b/README.rst @@ -104,10 +104,7 @@ Note about types of times and binaryData ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - By default time parameters (e.g. **cloudUpdateTime**, **deviceAckTime**, **updateTime**) are returned as **RFC3339** strings (e.g. "2023-01-12T23:38:07.732Z"). -- To return times formatted as **DatetimeWithNanoseconds** (defined in the **google.api_core.datetime_helpers** module) as returned by the **Google IoTCore Python SDK**: -1. Run **pip install google-api-core** to install appropriate type. -2. Set environment variable **TIME_FORMAT** to exactly **datetimewithnanoseconds**. - +- To return times formatted as **DatetimeWithNanoseconds** (defined in the **proto.datetime_helpers** module) as returned by the **Google IoTCore Python SDK** set environment variable **TIME_FORMAT** to exactly **datetimewithnanoseconds**. - By default **CONFIG binaryData** is returned as a **base64-encoded string** and **STATE binaryData** is returned as a **NON-base64-encoded** string. - To return CONFIG and STATE binaryData as **BYTE ARRAYS** as returned by the Google IoTCore Python SDK, set environment variable **BINARYDATA_FORMAT** to exactly **bytes**. - If these environment variables are not set, or are set to any unexpeced values, then the default formats are returned. \ No newline at end of file From 317d6bb46c5578dab3f7030edc1cb9286f97af53 Mon Sep 17 00:00:00 2001 From: skysharma Date: Wed, 22 Feb 2023 17:45:12 -0600 Subject: [PATCH 10/18] Added "proto.datetime_helpers" to dependencies --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0fac11d2..f6c1113b 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ description = "Cloud IoT API client library" version = "1.0.5" release_status = "Development Status :: 5 - Production/Stable" -dependencies = ["httpx"] +dependencies = ["httpx", "proto.datetime_helpers"] package_root = os.path.abspath(os.path.dirname(__file__)) From 007835e91bffe24962f266d0896c59e25c58532b Mon Sep 17 00:00:00 2001 From: skysharma Date: Wed, 22 Feb 2023 17:46:15 -0600 Subject: [PATCH 11/18] Changed 'google.api_core' to 'proto.' --- clearblade/cloud/iot_v1/device_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clearblade/cloud/iot_v1/device_types.py b/clearblade/cloud/iot_v1/device_types.py index 2f2dd9de..a2eb5ac3 100644 --- a/clearblade/cloud/iot_v1/device_types.py +++ b/clearblade/cloud/iot_v1/device_types.py @@ -31,7 +31,7 @@ from .resources import GatewayType, LogLevel from .utils import get_value import os -from google.api_core.datetime_helpers import DatetimeWithNanoseconds +from proto.datetime_helpers import DatetimeWithNanoseconds import base64 class Device(): From 015fc70ce1a713780b129f4011e96e3730ec901b Mon Sep 17 00:00:00 2001 From: skysharma Date: Fri, 24 Feb 2023 11:42:35 -0600 Subject: [PATCH 12/18] Removed call to set 'convert_binarydata_to_bytes' in Devices.from_json --- clearblade/cloud/iot_v1/device_types.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/clearblade/cloud/iot_v1/device_types.py b/clearblade/cloud/iot_v1/device_types.py index a2eb5ac3..6b19ba42 100644 --- a/clearblade/cloud/iot_v1/device_types.py +++ b/clearblade/cloud/iot_v1/device_types.py @@ -91,8 +91,6 @@ def from_json(json): last_config_send_time = lastConfigSendTimeFromJson last_error_time = lastErrorTimeFromJson - convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_FORMAT") == None else os.environ.get("BINARYDATA_FORMAT").lower() == "bytes") - deviceConfig = DeviceConfig.from_json(get_value(json, 'config')) config = { "version": deviceConfig.version, "cloudUpdateTime": deviceConfig.cloud_update_time } if (deviceConfig.binary_data not in [None, ""]): From 0be054adf6a1ad7ceb504e831c65beb53fe68f3b Mon Sep 17 00:00:00 2001 From: skysharma Date: Mon, 27 Feb 2023 12:44:37 -0600 Subject: [PATCH 13/18] Changed README to reflect decision on ONE env. var --- README.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 1e3d5713..978a475c 100644 --- a/README.rst +++ b/README.rst @@ -103,8 +103,12 @@ Next Steps Note about types of times and binaryData ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- By default time parameters (e.g. **cloudUpdateTime**, **deviceAckTime**, **updateTime**) are returned as **RFC3339** strings (e.g. "2023-01-12T23:38:07.732Z"). -- To return times formatted as **DatetimeWithNanoseconds** (defined in the **proto.datetime_helpers** module) as returned by the **Google IoTCore Python SDK** set environment variable **TIME_FORMAT** to exactly **datetimewithnanoseconds**. -- By default **CONFIG binaryData** is returned as a **base64-encoded string** and **STATE binaryData** is returned as a **NON-base64-encoded** string. -- To return CONFIG and STATE binaryData as **BYTE ARRAYS** as returned by the Google IoTCore Python SDK, set environment variable **BINARYDATA_FORMAT** to exactly **bytes**. -- If these environment variables are not set, or are set to any unexpeced values, then the default formats are returned. \ No newline at end of file +- By default the following parameters are returned as the shown types: +1. All time parameters (e.g. **cloudUpdateTime**, **deviceAckTime**, **updateTime**): **RFC3339** strings (e.g. "2023-01-12T23:38:07.732Z") +2. **CONFIG binaryData**: **base64-encoded string** +3. **STATE binaryData**: **NON-base64-encoded string**. + +- To return these parameters using the same types used by the **Google IoTCore Python SDK**, set environment variable **BINARYDATA_AND_TIME_GOOGLE_FORMAT** to **True** (case-insensitive string). This will ensure the following parameters are returned as the shown types: +1. All times: **DatetimeWithNanoseconds** (defined in the **proto.datetime_helpers** module) +2. All **binaryData** (CONFIG, STATE etc.): **BYTE ARRAYS** +- If this environment variable is not set, or is set to any unexpeced values, then the default types listed previously are used. \ No newline at end of file From e758f75a82a4464dc2c309893c39f22bada1840a Mon Sep 17 00:00:00 2001 From: skysharma Date: Mon, 27 Feb 2023 12:52:09 -0600 Subject: [PATCH 14/18] Changed module to import to proto-plus --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index f6c1113b..4bf8c8da 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ description = "Cloud IoT API client library" version = "1.0.5" release_status = "Development Status :: 5 - Production/Stable" -dependencies = ["httpx", "proto.datetime_helpers"] +dependencies = ["httpx", "proto-plus"] package_root = os.path.abspath(os.path.dirname(__file__)) From e03b48a073287b486c7112e2b3ad0abb7351dcc9 Mon Sep 17 00:00:00 2001 From: skysharma Date: Mon, 27 Feb 2023 12:55:13 -0600 Subject: [PATCH 15/18] Two env. vars -> BINARYDATA_AND_TIME_GOOGLE_FORMAT --- clearblade/cloud/iot_v1/device_types.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clearblade/cloud/iot_v1/device_types.py b/clearblade/cloud/iot_v1/device_types.py index 6b19ba42..71832484 100644 --- a/clearblade/cloud/iot_v1/device_types.py +++ b/clearblade/cloud/iot_v1/device_types.py @@ -75,7 +75,7 @@ def from_json(json): lastConfigSendTimeFromJson = get_value(json, 'lastConfigSendTime') lastErrorTimeFromJson = get_value(json, 'lastErrorTime') - convert_times_to_datetime_with_nanoseconds = (False if os.environ.get("TIME_FORMAT") == None else os.environ.get("TIME_FORMAT").lower() == "datetimewithnanoseconds") + convert_times_to_datetime_with_nanoseconds = (False if os.environ.get("BINARYDATA_AND_TIME_GOOGLE_FORMAT") == None else os.environ.get("BINARYDATA_AND_TIME_GOOGLE_FORMAT").lower() == "true") if convert_times_to_datetime_with_nanoseconds: last_heartbeat_time = None if lastHeartbeatTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(lastHeartbeatTimeFromJson) last_event_time = None if lastEventTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(lastEventTimeFromJson) @@ -209,14 +209,14 @@ def from_json(response_json): updateTimeFromJson = get_value(response_json, 'updateTime') binaryDataFromJson = get_value(response_json, 'binaryData') - convert_times_to_datetime_with_nanoseconds = (False if os.environ.get("TIME_FORMAT") == None else os.environ.get("TIME_FORMAT").lower() == "datetimewithnanoseconds") + convert_times_to_datetime_with_nanoseconds = (False if os.environ.get("BINARYDATA_AND_TIME_GOOGLE_FORMAT") == None else os.environ.get("BINARYDATA_AND_TIME_GOOGLE_FORMAT").lower() == "true") if convert_times_to_datetime_with_nanoseconds: update_time = None if updateTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(updateTimeFromJson) else: update_time = updateTimeFromJson if (binaryDataFromJson not in [None, ""]): - convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_FORMAT") == None else os.environ.get("BINARYDATA_FORMAT").lower() == "bytes") + convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_AND_TIME_GOOGLE_FORMAT") == None else os.environ.get("BINARYDATA_AND_TIME_GOOGLE_FORMAT").lower() == "true") if convert_binarydata_to_bytes: binary_data = binaryDataFromJson.encode('utf-8') else: @@ -316,7 +316,7 @@ def from_json(json): deviceAckTimeFromJson = get_value(json, 'deviceAckTime') binaryDataFromJson = get_value(json,'binaryData') - convert_times_to_datetime_with_nanoseconds = (False if os.environ.get("TIME_FORMAT") == None else os.environ.get("TIME_FORMAT").lower() == "datetimewithnanoseconds") + convert_times_to_datetime_with_nanoseconds = (False if os.environ.get("BINARYDATA_AND_TIME_GOOGLE_FORMAT") == None else os.environ.get("BINARYDATA_AND_TIME_GOOGLE_FORMAT").lower() == "true") if convert_times_to_datetime_with_nanoseconds: cloud_update_time = None if cloudUpdateTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(cloudUpdateTimeFromJson) device_ack_time = None if deviceAckTimeFromJson in [None, ""] else DatetimeWithNanoseconds.from_rfc3339(deviceAckTimeFromJson) @@ -325,7 +325,7 @@ def from_json(json): device_ack_time = deviceAckTimeFromJson if binaryDataFromJson not in [None, ""]: - convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_FORMAT") == None else os.environ.get("BINARYDATA_FORMAT").lower() == "bytes") + convert_binarydata_to_bytes = (False if os.environ.get("BINARYDATA_AND_TIME_GOOGLE_FORMAT") == None else os.environ.get("BINARYDATA_AND_TIME_GOOGLE_FORMAT").lower() == "true") if convert_binarydata_to_bytes: binary_data = base64.b64decode(binaryDataFromJson.encode('utf-8')) else: From 2d143989282b51a2f67a9bfb15b0f6542fa9b253 Mon Sep 17 00:00:00 2001 From: skysharma Date: Mon, 27 Feb 2023 12:56:38 -0600 Subject: [PATCH 16/18] Removed a '.' --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 978a475c..2267080c 100644 --- a/README.rst +++ b/README.rst @@ -106,7 +106,7 @@ Note about types of times and binaryData - By default the following parameters are returned as the shown types: 1. All time parameters (e.g. **cloudUpdateTime**, **deviceAckTime**, **updateTime**): **RFC3339** strings (e.g. "2023-01-12T23:38:07.732Z") 2. **CONFIG binaryData**: **base64-encoded string** -3. **STATE binaryData**: **NON-base64-encoded string**. +3. **STATE binaryData**: **NON-base64-encoded string** - To return these parameters using the same types used by the **Google IoTCore Python SDK**, set environment variable **BINARYDATA_AND_TIME_GOOGLE_FORMAT** to **True** (case-insensitive string). This will ensure the following parameters are returned as the shown types: 1. All times: **DatetimeWithNanoseconds** (defined in the **proto.datetime_helpers** module) From 57c366f6689f38bef2c20b909a44485a958f5419 Mon Sep 17 00:00:00 2001 From: skysharma Date: Mon, 27 Feb 2023 12:58:40 -0600 Subject: [PATCH 17/18] Format improvements for README --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 2267080c..b78eaab8 100644 --- a/README.rst +++ b/README.rst @@ -104,11 +104,15 @@ Note about types of times and binaryData ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - By default the following parameters are returned as the shown types: + 1. All time parameters (e.g. **cloudUpdateTime**, **deviceAckTime**, **updateTime**): **RFC3339** strings (e.g. "2023-01-12T23:38:07.732Z") 2. **CONFIG binaryData**: **base64-encoded string** 3. **STATE binaryData**: **NON-base64-encoded string** + - To return these parameters using the same types used by the **Google IoTCore Python SDK**, set environment variable **BINARYDATA_AND_TIME_GOOGLE_FORMAT** to **True** (case-insensitive string). This will ensure the following parameters are returned as the shown types: + 1. All times: **DatetimeWithNanoseconds** (defined in the **proto.datetime_helpers** module) 2. All **binaryData** (CONFIG, STATE etc.): **BYTE ARRAYS** + - If this environment variable is not set, or is set to any unexpeced values, then the default types listed previously are used. \ No newline at end of file From 9dae187da26e38f9d869a490642adf735256c5f8 Mon Sep 17 00:00:00 2001 From: skysharma Date: Mon, 27 Feb 2023 13:29:29 -0600 Subject: [PATCH 18/18] Added note about new env. var in 'Quick Start' sec --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b78eaab8..9e7a1b2a 100644 --- a/README.rst +++ b/README.rst @@ -30,8 +30,9 @@ In order to use this library, you first need to go through the following steps: 1. Install pip package - ```pip install clearblade-cloud-iot``` -2. Set an environment variable CLEARBLADE_CONFIGURATION which should point to your clearblade service account json file. +2. Set an environment variable **CLEARBLADE_CONFIGURATION** which should point to your clearblade service account json file. +3. Optionally set an environment variable **BINARYDATA_AND_TIME_GOOGLE_FORMAT** to True. Look at **Note about types of times and binaryData** below for details. Installation ~~~~~~~~~~~~