Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -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
================================================================

Expand Down Expand Up @@ -78,3 +99,12 @@ 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
Copy link
Collaborator

@rajasd27 rajasd27 Feb 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add this Note as the 3rd point in the Quickstart section? That way people can get to know that they have the option to set this env variable along with the CLEARBLADE_CONFIGURATION.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Added to the 'Quick Start'. Take a look.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- 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.
141 changes: 127 additions & 14 deletions clearblade/cloud/iot_v1/device_types.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
# -*- 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

import os
from proto.datetime_helpers import DatetimeWithNanoseconds
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also looks third party, got this error when executed SDK sample
ModuleNotFoundError: No module named 'proto'

import base64

class Device():
"""
Expand Down Expand Up @@ -37,14 +68,57 @@ 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

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):
Expand Down Expand Up @@ -132,8 +206,26 @@ 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

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

return DeviceState(update_time=update_time,
binary_data=binary_data)


class Request():
Expand Down Expand Up @@ -220,11 +312,32 @@ 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

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=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):
Expand Down
31 changes: 29 additions & 2 deletions clearblade/cloud/iot_v1/devices.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
import base64

# -*- 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
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure if this would work. I think you'll need to add the exact pip package proto-plus in this case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'll make that change.


package_root = os.path.abspath(os.path.dirname(__file__))

Expand Down