Skip to content

Commit

Permalink
Example clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
djesic committed Jul 28, 2021
1 parent bbc3c3a commit 2319c7e
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 127 deletions.
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,13 @@ wolk_device = wolk.WolkConnect(
wolk_device.connect()
```

### Adding sensor readings
### Adding feed values

```python
wolk_device.add_sensor_reading("T", 26.93)
wolk_device.add_feed_value(("T", 26.93))

# Multi-value sensor reading
wolk_device.add_sensor_reading("ACL", (4, 2, 0))
```
or multiple sensors at once with `add_sensor_readings`:
```python
wolk_device.add_sensor_readings({"T": 26.93, "ACL": (4, 2, 0)})
# or multiple feed value readings
wolk_device.add_feed_value([("T": 27.11), ("H": 54.34), ("P", 1002.3)])
```

Optionally pass a `timestamp` as `round(time.time()) * 1000`.
Expand All @@ -95,7 +91,7 @@ If `timestamp` is not provided, the library will assign a timestamp before placi

### Data publish strategy

Stored sensor readings are pushed to WolkAbout IoT platform on demand by calling:
Stored feed values are pushed to WolkAbout IoT platform on demand by calling:
```python
wolk_device.publish()
```
Expand Down
68 changes: 8 additions & 60 deletions examples/full_feature_set/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from traceback import print_exc
from typing import Dict
from typing import Optional
from typing import Tuple
from typing import Union

import requests
Expand Down Expand Up @@ -112,59 +111,32 @@ def main() -> None:
Pass all of these to a WolkConnect class
and start a loop to send different types of random readings.
"""
# TODO: Update this example
print("TODO: Update this example")
return

# Insert the device credentials received
# from WolkAbout IoT Platform when creating the device
# List actuator references included on your device
actuator_references = ["SW", "SL"]
device = wolk.Device(
key="device_key",
password="some_password",
actuator_references=actuator_references,
)
device = wolk.Device(key="device_key", password="some_password")
try:
global configurations
with open(configuration_file) as file:
configurations = json.load(file)
wolk.logging_config(configurations["LL"]) # Log level
except Exception:
print(
"Failed load configuraiton options "
"Failed load configuration options "
f"from file '{configuration_file}'"
)
print_exc()
sys.exit(1)
raise RuntimeError

class Actuator:
class InOutFeed:
def __init__(
self, inital_value: Optional[Union[bool, int, float, str]]
):
self.value = inital_value

switch = Actuator(False)
slider = Actuator(0)

# Provide a way to read actuator status if your device has actuators
def actuator_status_provider(
reference: str,
) -> Tuple[wolk.State, Optional[Union[bool, int, float, str]]]:
if reference == actuator_references[0]:
return wolk.State.READY, switch.value
elif reference == actuator_references[1]:
return wolk.State.READY, slider.value

return wolk.State.ERROR, None

# Provide an actuation handler if your device has actuators
def actuation_handler(
reference: str, value: Union[bool, int, float, str]
) -> None:
print(f"Setting actuator '{reference}' to value: {value}")
if reference == actuator_references[0]:
switch.value = value

elif reference == actuator_references[1]:
slider.value = value

# The URL download implementation can be substituted
# This is optional and passed as an argument when creating wolk_device
def url_download(file_url: str, file_path: str) -> bool:
Expand Down Expand Up @@ -210,14 +182,6 @@ def get_current_version(self) -> str:
port=8883,
ca_cert=".." + os.sep + ".." + os.sep + "wolk" + os.sep + "ca.crt",
)
.with_actuators(
actuation_handler=actuation_handler,
actuator_status_provider=actuator_status_provider,
)
.with_configuration(
configuration_handler=configuration_handler,
configuration_provider=configuration_provider,
)
.with_file_management(
preferred_package_size=1000 * 1000,
max_file_size=100 * 1000 * 1000,
Expand All @@ -235,13 +199,6 @@ def get_current_version(self) -> str:
print("Connecting to WolkAbout IoT Platform")
wolk_device.connect()

# Successfully connecting to the platform will publish device configuration
# all actuator statuses, files present on device, current firmware version
# and the result of a firmware update if it occurred
wolk_device.publish_configuration()
wolk_device.publish_actuator_status("SW")
wolk_device.publish_actuator_status("SL")

publish_period_seconds = configurations["HB"] # Heart beat

while True:
Expand All @@ -253,11 +210,6 @@ def get_current_version(self) -> str:
temperature = random.uniform(15, 30)
humidity = random.uniform(10, 55)
pressure = random.uniform(975, 1030)
accelerometer = (
random.uniform(0, 100),
random.uniform(0, 100),
random.uniform(0, 100),
)

# Enabled feeds
if "T" in configurations["EF"]:
Expand All @@ -271,10 +223,6 @@ def get_current_version(self) -> str:
wolk_device.add_alarm("HH", False)
if "P" in configurations["EF"]:
wolk_device.add_sensor_reading("P", pressure, timestamp)
if "ACL" in configurations["EF"]:
wolk_device.add_sensor_reading(
"ACL", accelerometer, timestamp
)

else:
wolk_device.connect()
Expand Down
35 changes: 7 additions & 28 deletions examples/simple/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,49 +23,28 @@

# NOTE: Enable debug logging by uncommenting the following line
# Optionally, as a second argument pass a file name
wolk.logging_config("debug")
# wolk.logging_config("debug")


def main():
"""Connect to WolkAbout IoT Platform and send a random sensor reading."""
# Insert the device credentials received
# from WolkAbout IoT Platform when creating the device
device = wolk.Device(
key="danilo_pull_dev",
password="AIGAYDA51S",
data_delivery=wolk.DataDelivery.PULL,
)
device = wolk.Device(key="some_key", password="some_password")

def incoming_feed_value_handler(feed_values):
for feed_value in feed_values:
for reference, value in feed_value.items():
if reference == "timestamp":
continue

if reference == "SL":
print("[dummy] setting SL to: " + value)

wolk_device = wolk.WolkConnect(
device,
host="10.0.50.168",
port=1883,
).with_incoming_feed_value_handler(incoming_feed_value_handler)
wolk_device = wolk.WolkConnect(device)

# Establish a connection to the WolkAbout IoT Platform
print("Connecting to WolkAbout IoT Platform")
wolk_device.connect()

# NOTE: Connect calls these implicitly
# wolk_device.pull_parameters()
# wolk_device.pull_feed_values()

publish_period_seconds = 45
publish_period_seconds = 60

while True:
try:
slider = random.randint(-20, 80)
wolk_device.add_feed_value("SL", slider)
print('Publishing "SL": ' + str(slider))
temperature = random.randint(-20, 80)
wolk_device.add_feed_value(("T", temperature))
print(f'Publishing "T": {temperature}')
wolk_device.publish()
time.sleep(publish_period_seconds)
except KeyboardInterrupt:
Expand Down
30 changes: 28 additions & 2 deletions test/test_wolkabout_protocol_message_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,30 @@ def test_init(self):
"""Test that object is created with correct device key."""
self.assertEqual(self.device_key, self.factory.device_key)

def test_feed_value(self):
"""Test valid message for string feed."""
reference = "SNL"
value = "string"
timestamp = round(time.time()) * 1000

expected_topic = self.factory.common_topic + WAPMF.FEED_VALUES
expected_payload = json.dumps(
[{reference: value, "timestamp": timestamp}]
)
expected_message = Message(expected_topic, expected_payload)

serialized_message = self.factory.make_from_feed_value(
(reference, value), timestamp
)

self.assertEqual(expected_message, serialized_message)

def test_feed_values(self):
"""Test valid message for string with newline sensor reading."""
"""Test valid message for two string feeds."""
reference = "SNL"
value = "string"
reference_2 = "SNL"
value_2 = "string"
timestamp = round(time.time()) * 1000

expected_topic = self.factory.common_topic + WAPMF.FEED_VALUES
Expand All @@ -61,11 +81,17 @@ def test_feed_values(self):
expected_message = Message(expected_topic, expected_payload)

serialized_message = self.factory.make_from_feed_value(
reference, value, timestamp
[(reference, value), (reference_2, value_2)], timestamp
)

self.assertEqual(expected_message, serialized_message)

def test_feed_value_throws_on_invalid_data(self):
"""Test valid message for two string feeds."""
self.assertRaises(
ValueError, self.factory.make_from_feed_value, "foo", 1
)

def test_file_list_update(self):
"""Test message for file list update."""
file_list = ["file1.txt", "file2.bin", "file3 with spaces.jpg"]
Expand Down
13 changes: 7 additions & 6 deletions wolk/interface/message_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from typing import Dict
from typing import List
from typing import Optional
from typing import Tuple
from typing import Union

from wolk.model.data_type import DataType
Expand All @@ -26,24 +27,24 @@
from wolk.model.message import Message
from wolk.model.unit import Unit

OutgoingDataTypes = Union[bool, int, float, str]
Reading = Tuple[str, OutgoingDataTypes]


class MessageFactory(ABC):
"""Serialize messages to be sent to WolkAbout IoT Platform."""

@abstractmethod
def make_from_feed_value(
self,
reference: str,
value: Union[bool, int, float, str],
reading: Union[Reading, List[Reading]],
timestamp: Optional[int],
) -> Message:
"""
Serialize feed value data.
:param reference: Feed identifier
:type reference: str
:param value: Value of the feed
:type value: Union[bool, int, float, str]
:param reading: Feed value data as (reference, value) or list of tuple
:type reading: Union[Reading, List[Reading]]
:param timestamp: Unix timestamp in ms. Default to current time if None
:returns: message
:rtype: Message
Expand Down
34 changes: 21 additions & 13 deletions wolk/wolk_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from typing import Dict
from typing import List
from typing import Optional
from typing import Tuple
from typing import Union

from wolk import logger_factory
Expand Down Expand Up @@ -52,6 +53,8 @@
)

IncomingData = List[Dict[str, Union[bool, int, float, str]]]
OutgoingDataTypes = Union[bool, int, float, str]
Reading = Tuple[str, OutgoingDataTypes]


class WolkConnect:
Expand Down Expand Up @@ -361,28 +364,33 @@ def disconnect(self) -> None:

def add_feed_value(
self,
reference: str,
value: Union[bool, int, float, str],
reading: Union[Reading, List[Reading]],
timestamp: Optional[int] = None,
) -> None:
"""
Place a feed value into storage.
Place a feed value reading into storage.
:param reference: Feed reference
:type reference: str
:param value: Value of the feed
:type value: Union[bool, int, float, str]
:param timestamp: Unix timestamp. If not provided, library will assign
A reading is identified by a unique feed reference string and
the current value of the feed.
This reading can either be passed as a tuple of (reference, value)
for a single feed or as a list of previously mentioned tuples
to pass multiple feed readings at once.
A Unix epoch timestamp in milliseconds as int can be provided to
denote when the reading occurred. By default, the current system
provided time will be assigned to a reading.
:param reading: Feed value reading
:type reading: Union[Reading, List[Reading]]
:param timestamp: Unix timestamp. Defaults to system time.
:type timestamp: Optional[int]
"""
self.logger.debug(
f"Adding feed value: reference = '{reference}', "
f"value = {value}, timestamp = {timestamp}"
f"Adding feed value: reading: {reading}, timestamp = {timestamp}"
)

message = self.message_factory.make_from_feed_value(
reference, value, timestamp
)
message = self.message_factory.make_from_feed_value(reading, timestamp)
# NOTE: if device is PUSH, do we try to publish instantly?
self.message_queue.put(message)

Expand Down
Loading

0 comments on commit 2319c7e

Please sign in to comment.