<reponame>StutiSri/smart-plant-care
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
import os
import asyncio
import random
import logging
import json

from azure.iot.device.aio import IoTHubDeviceClient
from azure.iot.device.aio import ProvisioningDeviceClient
from azure.iot.device import MethodResponse
from datetime import timedelta, datetime
import pnp_helper


############################SENSOR CODE######################
import time
import busio
import digitalio
import board
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

# create the spi bus
spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)

# create the cs (chip select)
cs = digitalio.DigitalInOut(board.D22)

# create the mcp object
mcp = MCP.MCP3008(spi, cs)

# create an analog input channel on pin 0
chan0 = AnalogIn(mcp, MCP.P0)
chan1 = AnalogIn(mcp, MCP.P1)

#print('Raw ADC Value: ', chan0.value)
#print('ADC Voltage: ' + str(chan0.voltage) + 'V')

last_read = 0       # this keeps track of the last potentiometer value
tolerance = 250     # to keep from being jittery we'll only change
                    # volume when the pot has moved a significant amount
                    # on a 16-bit ADC

##############################SENSOR CODE####################

logging.basicConfig(level=logging.ERROR)

# the interfaces that are pulled in to implement the device.
# User has to know these values as these may change and user can
# choose to implement different interfaces.
thermostat_digital_twin_model_identifier = "dtmi:com:example:Thermostat;1"
device_info_digital_twin_model_identifier = "dtmi:azure:DeviceManagement:DeviceInformation;1"

# The device "TemperatureController" that is getting implemented using the above interfaces.
# This id can change according to the company the user is from
# and the name user wants to call this Plug and Play device
model_id = "dtmi:com:example:TemperatureController;2"

# the components inside this Plug and Play device.
# there can be multiple components from 1 interface
# component names according to interfaces following pascal case.
device_information_component_name = "deviceInformation"
thermostat_1_component_name = "thermostat1"
thermostat_2_component_name = "thermostat2"
serial_number = "alohomora"
#####################################################
# COMMAND HANDLERS : User will define these handlers
# depending on what commands the component defines

#####################################################
# GLOBAL VARIABLES
THERMOSTAT_1 = None
THERMOSTAT_2 = None

def remap_range(value, left_min, left_max, right_min, right_max):
    # this remaps a value from original (left) range to new (right) range
    # Figure out how 'wide' each range is
    left_span = left_max - left_min
    right_span = right_max - right_min

    # Convert the left range into a 0-1 range (int)
    valueScaled = int(value - left_min) / int(left_span)

    # Convert the 0-1 range into a value in the right range.
    return int(right_min + (valueScaled * right_span))

class Thermostat(object):
    def __init__(self, name, moving_win=10):

        self.moving_window = moving_win
        self.records = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        self.index = 0

        self.cur = 0
        self.max = 0
        self.min = 0
        self.avg = 0

        self.name = name

    def record(self, current_temp):
        self.cur = current_temp
        self.records[self.index] = current_temp
        self.max = self.calculate_max(current_temp)
        self.min = self.calculate_min(current_temp)
        self.avg = self.calculate_average()

        self.index = (self.index + 1) % self.moving_window

    def calculate_max(self, current_temp):
        if not self.max:
            return current_temp
        elif current_temp > self.max:
            return self.max

    def calculate_min(self, current_temp):
        if not self.min:
            return current_temp
        elif current_temp < self.min:
            return self.min

    def calculate_average(self):
        return sum(self.records) / self.moving_window

    def create_report(self):
        response_dict = {}
        response_dict["maxTemp"] = self.max
        response_dict["minTemp"] = self.min
        response_dict["avgTemp"] = self.avg
        response_dict["startTime"] = (
            (datetime.now() - timedelta(0, self.moving_window * 8)).astimezone().isoformat()
        )
        response_dict["endTime"] = datetime.now().astimezone().isoformat()
        return response_dict


async def reboot_handler(values):
    if values:
        print("Rebooting after delay of {delay} secs".format(delay=values))
    print("Done rebooting")


async def max_min_handler(values):
    if values:
        print(
            "Will return the max, min and average temperature from the specified time {since} to the current time".format(
                since=values
            )
        )
    print("Done generating")


# END COMMAND HANDLERS
#####################################################

#####################################################
# CREATE RESPONSES TO COMMANDS


def create_max_min_report_response(thermostat_name):
    """
    An example function that can create a response to the "getMaxMinReport" command request the way the user wants it.
    Most of the times response is created by a helper function which follows a generic pattern.
    This should be only used when the user wants to give a detailed response back to the Hub.
    :param values: The values that were received as part of the request.
    """
    if "Thermostat;1" in thermostat_name and THERMOSTAT_1:
        response_dict = THERMOSTAT_1.create_report()
    elif THERMOSTAT_2:
        response_dict = THERMOSTAT_2.create_report()
    else:  # This is done to pass certification.
        response_dict = {}
        response_dict["maxTemp"] = 0
        response_dict["minTemp"] = 0
        response_dict["avgTemp"] = 0
        response_dict["startTime"] = datetime.now().astimezone().isoformat()
        response_dict["endTime"] = datetime.now().astimezone().isoformat()

    print(response_dict)
    return response_dict


# END CREATE RESPONSES TO COMMANDS
#####################################################

#####################################################
# TELEMETRY TASKS


async def send_telemetry_from_temp_controller(device_client, telemetry_msg, component_name=None):
    msg = pnp_helper.create_telemetry(telemetry_msg, component_name)
    await device_client.send_message(msg)
    print("Sent message")
    print(msg)
    await asyncio.sleep(5)


#####################################################
# COMMAND TASKS


async def execute_command_listener(
    device_client,
    component_name=None,
    method_name=None,
    user_command_handler=None,
    create_user_response_handler=None,
):
    """
    Coroutine for executing listeners. These will listen for command requests.
    They will take in a user provided handler and call the user provided handler
    according to the command request received.
    :param device_client: The device client
    :param component_name: The name of the device like "sensor"
    :param method_name: (optional) The specific method name to listen for. Eg could be "blink", "turnon" etc.
    If not provided the listener will listen for all methods.
    :param user_command_handler: (optional) The user provided handler that needs to be executed after receiving "command requests".
    If not provided nothing will be executed on receiving command.
    :param create_user_response_handler: (optional) The user provided handler that will create a response.
    If not provided a generic response will be created.
    :return:
    """
    while True:
        if component_name and method_name:
            command_name = component_name + "*" + method_name
        elif method_name:
            command_name = method_name
        else:
            command_name = None

        command_request = await device_client.receive_method_request(command_name)
        print("Command request received with payload")
        values = command_request.payload
        print(values)

        if user_command_handler:
            await user_command_handler(values)
        else:
            print("No handler provided to execute")

        (response_status, response_payload) = pnp_helper.create_response_payload_with_status(
            command_request, method_name, create_user_response=create_user_response_handler
        )

        command_response = MethodResponse.create_from_method_request(
            command_request, response_status, response_payload
        )

        try:
            await device_client.send_method_response(command_response)
        except Exception:
            print("responding to the {command} command failed".format(command=method_name))


#####################################################
# PROPERTY TASKS


async def execute_property_listener(device_client):
    while True:
        patch = await device_client.receive_twin_desired_properties_patch()  # blocking call
        print(patch)
        properties_dict = pnp_helper.create_reported_properties_from_desired(patch)

        await device_client.patch_twin_reported_properties(properties_dict)


#####################################################
# An # END KEYBOARD INPUT LISTENER to quit application


def stdin_listener():
    """
    Listener for quitting the sample
    """
    while True:
        selection = input("Press Q to quit\n")
        if selection == "Q" or selection == "q":
            print("Quitting...")
            break


# END KEYBOARD INPUT LISTENER
#####################################################


#####################################################
# MAIN STARTS
async def provision_device(provisioning_host, id_scope, registration_id, symmetric_key, model_id):
    provisioning_device_client = ProvisioningDeviceClient.create_from_symmetric_key(
        provisioning_host=provisioning_host,
        registration_id=registration_id,
        id_scope=id_scope,
        symmetric_key=symmetric_key,
    )

    provisioning_device_client.provisioning_payload = {"modelId": model_id}
    return await provisioning_device_client.register()


async def main():
    switch = os.getenv("IOTHUB_DEVICE_SECURITY_TYPE")
    if switch == "DPS":
        provisioning_host = (
            os.getenv("IOTHUB_DEVICE_DPS_ENDPOINT")
            if os.getenv("IOTHUB_DEVICE_DPS_ENDPOINT")
            else "global.azure-devices-provisioning.net"
        )
        id_scope = os.getenv("IOTHUB_DEVICE_DPS_ID_SCOPE")
        registration_id = os.getenv("IOTHUB_DEVICE_DPS_DEVICE_ID")
        symmetric_key = os.getenv("IOTHUB_DEVICE_DPS_DEVICE_KEY")

        registration_result = await provision_device(
            provisioning_host, id_scope, registration_id, symmetric_key, model_id
        )

        if registration_result.status == "assigned":
            print("Device was assigned")
            print(registration_result.registration_state.assigned_hub)
            print(registration_result.registration_state.device_id)
            device_client = IoTHubDeviceClient.create_from_symmetric_key(
                symmetric_key=symmetric_key,
                hostname=registration_result.registration_state.assigned_hub,
                device_id=registration_result.registration_state.device_id,
                product_info=model_id,
            )
        else:
            raise RuntimeError(
                "Could not provision device. Aborting Plug and Play device connection."
            )

    elif switch == "connectionString":
        conn_str = os.getenv("IOTHUB_DEVICE_CONNECTION_STRING")
        print("Connecting using Connection String " + conn_str)
        device_client = IoTHubDeviceClient.create_from_connection_string(
            conn_str, product_info=model_id
        )
    else:
        raise RuntimeError(
            "At least one choice needs to be made for complete functioning of this sample."
        )

    # Connect the client.
    await device_client.connect()

    ################################################
    # Update readable properties from various components

    properties_root = pnp_helper.create_reported_properties(serialNumber=serial_number)
    properties_thermostat1 = pnp_helper.create_reported_properties(
        thermostat_1_component_name, maxTempSinceLastReboot=98.34
    )
    properties_thermostat2 = pnp_helper.create_reported_properties(
        thermostat_2_component_name, maxTempSinceLastReboot=48.92
    )
    properties_device_info = pnp_helper.create_reported_properties(
        device_information_component_name,
        swVersion="5.5",
        manufacturer="Contoso Device Corporation",
        model="Contoso 4762B-turbo",
        osName="Mac Os",
        processorArchitecture="x86-64",
        processorManufacturer="Intel",
        totalStorage=1024,
        totalMemory=32,
    )

    property_updates = asyncio.gather(
        device_client.patch_twin_reported_properties(properties_root),
        device_client.patch_twin_reported_properties(properties_thermostat1),
        device_client.patch_twin_reported_properties(properties_thermostat2),
        device_client.patch_twin_reported_properties(properties_device_info),
    )

    ################################################
    # Get all the listeners running
    print("Listening for command requests and property updates")

    global THERMOSTAT_1
    global THERMOSTAT_2
    THERMOSTAT_1 = Thermostat(thermostat_1_component_name, 10)
    THERMOSTAT_2 = Thermostat(thermostat_2_component_name, 10)

    listeners = asyncio.gather(
        execute_command_listener(
            device_client, method_name="reboot", user_command_handler=reboot_handler
        ),
        execute_command_listener(
            device_client,
            thermostat_1_component_name,
            method_name="getMaxMinReport",
            user_command_handler=max_min_handler,
            create_user_response_handler=create_max_min_report_response,
        ),
        execute_command_listener(
            device_client,
            thermostat_2_component_name,
            method_name="getMaxMinReport",
            user_command_handler=max_min_handler,
            create_user_response_handler=create_max_min_report_response,
        ),
        execute_property_listener(device_client),
    )

    ################################################
    # Function to send telemetry every 8 seconds

    async def send_telemetry():
        print("Sending telemetry from various components")

        while True:
            #curr_temp_ext = random.randrange(10, 50)
            #THERMOSTAT_1.record(curr_temp_ext)

#########################
            print("in while loop")
            luminosity_raw = chan0.value
            moisture_raw = chan1.value
            #print('hello')
#            print('Luminosity value = {luminosity}' .format(luminosity_t = luminosity_raw))

            moisture = 100 - remap_range(moisture_raw, 15000, 65535, 0, 100) 
            luminosity = 100 - remap_range(luminosity_raw, 4000, 65535, 0, 100)

    # how much has it changed since the last read?
#    pot_adjust = abs(trim_pot - last_read)

#    if pot_adjust > tolerance:
#        trim_pot_changed = True


#    if trim_pot_changed:
        # convert 16bit adc0 (0-65535) trim pot read into 0-100 volume level
        # set_volume = remap_range(trim_pot, 0, 65535, 0, 100)

        # set OS volume playback volume
            print('Moisture value = {moisture_t}' .format(moisture_t = moisture_raw))
            print('Luminosity value = {luminosity_t}' .format(luminosity_t = luminosity_raw))
#######################

            #temperature_msg1 = {"temperature": moisture}
            #await send_telemetry_from_temp_controller(
             #   device_client, temperature_msg1, thermostat_1_component_name
            #)

            #curr_temp_int = random.randrange(10, 50)  # Current temperature in Celsius
            #THERMOSTAT_2.record(curr_temp_int)

            temperature_msg2 = {"luminosity": luminosity, "moisture": moisture}

            await send_telemetry_from_temp_controller(
                device_client, temperature_msg2, thermostat_2_component_name
            )

            # workingset_msg3 = {"workingSet": random.randrange(1, 100)}
            # await send_telemetry_from_temp_controller(device_client, workingset_msg3)

    send_telemetry_task = asyncio.ensure_future(send_telemetry())

    # Run the stdin listener in the event loop
    loop = asyncio.get_running_loop()
    user_finished = loop.run_in_executor(None, stdin_listener)
    # # Wait for user to indicate they are done listening for method calls
    await user_finished

    if not listeners.done():
        listeners.set_result("DONE")

    if not property_updates.done():
        property_updates.set_result("DONE")

    listeners.cancel()
    property_updates.cancel()

    send_telemetry_task.cancel()

    # Finally, shut down the client
    await device_client.shutdown()


#####################################################
# EXECUTE MAIN

if __name__ == "__main__":
    asyncio.run(main())

    # If using Python 3.6 or below, use the following code instead of asyncio.run(main()):
    # loop = asyncio.get_event_loop()
    # loop.run_until_complete(main())
    # loop.close()

# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
import os
import asyncio
import random
import logging
import json

from azure.iot.device.aio import IoTHubDeviceClient
from azure.iot.device.aio import ProvisioningDeviceClient
from azure.iot.device import MethodResponse
from datetime import timedelta, datetime
import pnp_helper


############################SENSOR CODE######################
import time
import busio
import digitalio
import board
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

# create the spi bus
spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)

# create the cs (chip select)
cs = digitalio.DigitalInOut(board.D22)

# create the mcp object
mcp = MCP.MCP3008(spi, cs)

# create an analog input channel on pin 0
chan0 = AnalogIn(mcp, MCP.P0)
chan1 = AnalogIn(mcp, MCP.P1)

#print('Raw ADC Value: ', chan0.value)
#print('ADC Voltage: ' + str(chan0.voltage) + 'V')

last_read = 0       # this keeps track of the last potentiometer value
tolerance = 250     # to keep from being jittery we'll only change
                    # volume when the pot has moved a significant amount
                    # on a 16-bit ADC

##############################SENSOR CODE####################

logging.basicConfig(level=logging.ERROR)

# the interfaces that are pulled in to implement the device.
# User has to know these values as these may change and user can
# choose to implement different interfaces.
thermostat_digital_twin_model_identifier = "dtmi:com:example:Thermostat;1"
device_info_digital_twin_model_identifier = "dtmi:azure:DeviceManagement:DeviceInformation;1"

# The device "TemperatureController" that is getting implemented using the above interfaces.
# This id can change according to the company the user is from
# and the name user wants to call this Plug and Play device
model_id = "dtmi:com:example:TemperatureController;2"

# the components inside this Plug and Play device.
# there can be multiple components from 1 interface
# component names according to interfaces following pascal case.
device_information_component_name = "deviceInformation"
thermostat_1_component_name = "thermostat1"
thermostat_2_component_name = "thermostat2"
serial_number = "alohomora"
#####################################################
# COMMAND HANDLERS : User will define these handlers
# depending on what commands the component defines

#####################################################
# GLOBAL VARIABLES
THERMOSTAT_1 = None
THERMOSTAT_2 = None

def remap_range(value, left_min, left_max, right_min, right_max):
    # this remaps a value from original (left) range to new (right) range
    # Figure out how 'wide' each range is
    left_span = left_max - left_min
    right_span = right_max - right_min

    # Convert the left range into a 0-1 range (int)
    valueScaled = int(value - left_min) / int(left_span)

    # Convert the 0-1 range into a value in the right range.
    return int(right_min + (valueScaled * right_span))

class Thermostat(object):
    def __init__(self, name, moving_win=10):

        self.moving_window = moving_win
        self.records = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        self.index = 0

        self.cur = 0
        self.max = 0
        self.min = 0
        self.avg = 0

        self.name = name

    def record(self, current_temp):
        self.cur = current_temp
        self.records[self.index] = current_temp
        self.max = self.calculate_max(current_temp)
        self.min = self.calculate_min(current_temp)
        self.avg = self.calculate_average()

        self.index = (self.index + 1) % self.moving_window

    def calculate_max(self, current_temp):
        if not self.max:
            return current_temp
        elif current_temp > self.max:
            return self.max

    def calculate_min(self, current_temp):
        if not self.min:
            return current_temp
        elif current_temp < self.min:
            return self.min

    def calculate_average(self):
        return sum(self.records) / self.moving_window

    def create_report(self):
        response_dict = {}
        response_dict["maxTemp"] = self.max
        response_dict["minTemp"] = self.min
        response_dict["avgTemp"] = self.avg
        response_dict["startTime"] = (
            (datetime.now() - timedelta(0, self.moving_window * 8)).astimezone().isoformat()
        )
        response_dict["endTime"] = datetime.now().astimezone().isoformat()
        return response_dict


async def reboot_handler(values):
    if values:
        print("Rebooting after delay of {delay} secs".format(delay=values))
    print("Done rebooting")


async def max_min_handler(values):
    if values:
        print(
            "Will return the max, min and average temperature from the specified time {since} to the current time".format(
                since=values
            )
        )
    print("Done generating")


# END COMMAND HANDLERS
#####################################################

#####################################################
# CREATE RESPONSES TO COMMANDS


def create_max_min_report_response(thermostat_name):
    """
    An example function that can create a response to the "getMaxMinReport" command request the way the user wants it.
    Most of the times response is created by a helper function which follows a generic pattern.
    This should be only used when the user wants to give a detailed response back to the Hub.
    :param values: The values that were received as part of the request.
    """
    if "Thermostat;1" in thermostat_name and THERMOSTAT_1:
        response_dict = THERMOSTAT_1.create_report()
    elif THERMOSTAT_2:
        response_dict = THERMOSTAT_2.create_report()
    else:  # This is done to pass certification.
        response_dict = {}
        response_dict["maxTemp"] = 0
        response_dict["minTemp"] = 0
        response_dict["avgTemp"] = 0
        response_dict["startTime"] = datetime.now().astimezone().isoformat()
        response_dict["endTime"] = datetime.now().astimezone().isoformat()

    print(response_dict)
    return response_dict


# END CREATE RESPONSES TO COMMANDS
#####################################################

#####################################################
# TELEMETRY TASKS


async def send_telemetry_from_temp_controller(device_client, telemetry_msg, component_name=None):
    msg = pnp_helper.create_telemetry(telemetry_msg, component_name)
    await device_client.send_message(msg)
    print("Sent message")
    print(msg)
    await asyncio.sleep(5)


#####################################################
# COMMAND TASKS


async def execute_command_listener(
    device_client,
    component_name=None,
    method_name=None,
    user_command_handler=None,
    create_user_response_handler=None,
):
    """
    Coroutine for executing listeners. These will listen for command requests.
    They will take in a user provided handler and call the user provided handler
    according to the command request received.
    :param device_client: The device client
    :param component_name: The name of the device like "sensor"
    :param method_name: (optional) The specific method name to listen for. Eg could be "blink", "turnon" etc.
    If not provided the listener will listen for all methods.
    :param user_command_handler: (optional) The user provided handler that needs to be executed after receiving "command requests".
    If not provided nothing will be executed on receiving command.
    :param create_user_response_handler: (optional) The user provided handler that will create a response.
    If not provided a generic response will be created.
    :return:
    """
    while True:
        if component_name and method_name:
            command_name = component_name + "*" + method_name
        elif method_name:
            command_name = method_name
        else:
            command_name = None

        command_request = await device_client.receive_method_request(command_name)
        print("Command request received with payload")
        values = command_request.payload
        print(values)

        if user_command_handler:
            await user_command_handler(values)
        else:
            print("No handler provided to execute")

        (response_status, response_payload) = pnp_helper.create_response_payload_with_status(
            command_request, method_name, create_user_response=create_user_response_handler
        )

        command_response = MethodResponse.create_from_method_request(
            command_request, response_status, response_payload
        )

        try:
            await device_client.send_method_response(command_response)
        except Exception:
            print("responding to the {command} command failed".format(command=method_name))


#####################################################
# PROPERTY TASKS


async def execute_property_listener(device_client):
    while True:
        patch = await device_client.receive_twin_desired_properties_patch()  # blocking call
        print(patch)
        properties_dict = pnp_helper.create_reported_properties_from_desired(patch)

        await device_client.patch_twin_reported_properties(properties_dict)


#####################################################
# An # END KEYBOARD INPUT LISTENER to quit application


def stdin_listener():
    """
    Listener for quitting the sample
    """
    while True:
        selection = input("Press Q to quit\n")
        if selection == "Q" or selection == "q":
            print("Quitting...")
            break


# END KEYBOARD INPUT LISTENER
#####################################################


#####################################################
# MAIN STARTS
async def provision_device(provisioning_host, id_scope, registration_id, symmetric_key, model_id):
    provisioning_device_client = ProvisioningDeviceClient.create_from_symmetric_key(
        provisioning_host=provisioning_host,
        registration_id=registration_id,
        id_scope=id_scope,
        symmetric_key=symmetric_key,
    )

    provisioning_device_client.provisioning_payload = {"modelId": model_id}
    return await provisioning_device_client.register()


async def main():
    switch = os.getenv("IOTHUB_DEVICE_SECURITY_TYPE")
    if switch == "DPS":
        provisioning_host = (
            os.getenv("IOTHUB_DEVICE_DPS_ENDPOINT")
            if os.getenv("IOTHUB_DEVICE_DPS_ENDPOINT")
            else "global.azure-devices-provisioning.net"
        )
        id_scope = os.getenv("IOTHUB_DEVICE_DPS_ID_SCOPE")
        registration_id = os.getenv("IOTHUB_DEVICE_DPS_DEVICE_ID")
        symmetric_key = os.getenv("IOTHUB_DEVICE_DPS_DEVICE_KEY")

        registration_result = await provision_device(
            provisioning_host, id_scope, registration_id, symmetric_key, model_id
        )

        if registration_result.status == "assigned":
            print("Device was assigned")
            print(registration_result.registration_state.assigned_hub)
            print(registration_result.registration_state.device_id)
            device_client = IoTHubDeviceClient.create_from_symmetric_key(
                symmetric_key=symmetric_key,
                hostname=registration_result.registration_state.assigned_hub,
                device_id=registration_result.registration_state.device_id,
                product_info=model_id,
            )
        else:
            raise RuntimeError(
                "Could not provision device. Aborting Plug and Play device connection."
            )

    elif switch == "connectionString":
        conn_str = os.getenv("IOTHUB_DEVICE_CONNECTION_STRING")
        print("Connecting using Connection String " + conn_str)
        device_client = IoTHubDeviceClient.create_from_connection_string(
            conn_str, product_info=model_id
        )
    else:
        raise RuntimeError(
            "At least one choice needs to be made for complete functioning of this sample."
        )

    # Connect the client.
    await device_client.connect()

    ################################################
    # Update readable properties from various components

    properties_root = pnp_helper.create_reported_properties(serialNumber=serial_number)
    properties_thermostat1 = pnp_helper.create_reported_properties(
        thermostat_1_component_name, maxTempSinceLastReboot=98.34
    )
    properties_thermostat2 = pnp_helper.create_reported_properties(
        thermostat_2_component_name, maxTempSinceLastReboot=48.92
    )
    properties_device_info = pnp_helper.create_reported_properties(
        device_information_component_name,
        swVersion="5.5",
        manufacturer="Contoso Device Corporation",
        model="Contoso 4762B-turbo",
        osName="Mac Os",
        processorArchitecture="x86-64",
        processorManufacturer="Intel",
        totalStorage=1024,
        totalMemory=32,
    )

    property_updates = asyncio.gather(
        device_client.patch_twin_reported_properties(properties_root),
        device_client.patch_twin_reported_properties(properties_thermostat1),
        device_client.patch_twin_reported_properties(properties_thermostat2),
        device_client.patch_twin_reported_properties(properties_device_info),
    )

    ################################################
    # Get all the listeners running
    print("Listening for command requests and property updates")

    global THERMOSTAT_1
    global THERMOSTAT_2
    THERMOSTAT_1 = Thermostat(thermostat_1_component_name, 10)
    THERMOSTAT_2 = Thermostat(thermostat_2_component_name, 10)

    listeners = asyncio.gather(
        execute_command_listener(
            device_client, method_name="reboot", user_command_handler=reboot_handler
        ),
        execute_command_listener(
            device_client,
            thermostat_1_component_name,
            method_name="getMaxMinReport",
            user_command_handler=max_min_handler,
            create_user_response_handler=create_max_min_report_response,
        ),
        execute_command_listener(
            device_client,
            thermostat_2_component_name,
            method_name="getMaxMinReport",
            user_command_handler=max_min_handler,
            create_user_response_handler=create_max_min_report_response,
        ),
        execute_property_listener(device_client),
    )

    ################################################
    # Function to send telemetry every 8 seconds

    async def send_telemetry():
        print("Sending telemetry from various components")

        while True:
            #curr_temp_ext = random.randrange(10, 50)
            #THERMOSTAT_1.record(curr_temp_ext)

#########################
            print("in while loop")
            luminosity_raw = chan0.value
            moisture_raw = chan1.value
            #print('hello')
#            print('Luminosity value = {luminosity}' .format(luminosity_t = luminosity_raw))

            moisture = 100 - remap_range(moisture_raw, 15000, 65535, 0, 100) 
            luminosity = 100 - remap_range(luminosity_raw, 4000, 65535, 0, 100)

    # how much has it changed since the last read?
#    pot_adjust = abs(trim_pot - last_read)

#    if pot_adjust > tolerance:
#        trim_pot_changed = True


#    if trim_pot_changed:
        # convert 16bit adc0 (0-65535) trim pot read into 0-100 volume level
        # set_volume = remap_range(trim_pot, 0, 65535, 0, 100)

        # set OS volume playback volume
            print('Moisture value = {moisture_t}' .format(moisture_t = moisture_raw))
            print('Luminosity value = {luminosity_t}' .format(luminosity_t = luminosity_raw))
#######################

            #temperature_msg1 = {"temperature": moisture}
            #await send_telemetry_from_temp_controller(
             #   device_client, temperature_msg1, thermostat_1_component_name
            #)

            #curr_temp_int = random.randrange(10, 50)  # Current temperature in Celsius
            #THERMOSTAT_2.record(curr_temp_int)

            temperature_msg2 = {"luminosity": luminosity, "moisture": moisture}

            await send_telemetry_from_temp_controller(
                device_client, temperature_msg2, thermostat_2_component_name
            )

            # workingset_msg3 = {"workingSet": random.randrange(1, 100)}
            # await send_telemetry_from_temp_controller(device_client, workingset_msg3)

    send_telemetry_task = asyncio.ensure_future(send_telemetry())

    # Run the stdin listener in the event loop
    loop = asyncio.get_running_loop()
    user_finished = loop.run_in_executor(None, stdin_listener)
    # # Wait for user to indicate they are done listening for method calls
    await user_finished

    if not listeners.done():
        listeners.set_result("DONE")

    if not property_updates.done():
        property_updates.set_result("DONE")

    listeners.cancel()
    property_updates.cancel()

    send_telemetry_task.cancel()

    # Finally, shut down the client
    await device_client.shutdown()


#####################################################
# EXECUTE MAIN

if __name__ == "__main__":
    asyncio.run(main())

    # If using Python 3.6 or below, use the following code instead of asyncio.run(main()):
    # loop = asyncio.get_event_loop()
    # loop.run_until_complete(main())
    # loop.close()
# ------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- ############################SENSOR CODE###################### # create the spi bus # create the cs (chip select) # create the mcp object # create an analog input channel on pin 0 #print('Raw ADC Value: ', chan0.value) #print('ADC Voltage: ' + str(chan0.voltage) + 'V') # this keeps track of the last potentiometer value # to keep from being jittery we'll only change # volume when the pot has moved a significant amount # on a 16-bit ADC ##############################SENSOR CODE#################### # the interfaces that are pulled in to implement the device. # User has to know these values as these may change and user can # choose to implement different interfaces. # The device "TemperatureController" that is getting implemented using the above interfaces. # This id can change according to the company the user is from # and the name user wants to call this Plug and Play device # the components inside this Plug and Play device. # there can be multiple components from 1 interface # component names according to interfaces following pascal case. ##################################################### # COMMAND HANDLERS : User will define these handlers # depending on what commands the component defines ##################################################### # GLOBAL VARIABLES # this remaps a value from original (left) range to new (right) range # Figure out how 'wide' each range is # Convert the left range into a 0-1 range (int) # Convert the 0-1 range into a value in the right range. # END COMMAND HANDLERS ##################################################### ##################################################### # CREATE RESPONSES TO COMMANDS An example function that can create a response to the "getMaxMinReport" command request the way the user wants it.
     Most of the times response is created by a helper function which follows a generic pattern.
     This should be only used when the user wants to give a detailed response back to the Hub.
     :param values: The values that were received as part of the request. # This is done to pass certification. # END CREATE RESPONSES TO COMMANDS ##################################################### ##################################################### # TELEMETRY TASKS ##################################################### # COMMAND TASKS Coroutine for executing listeners. These will listen for command requests.
     They will take in a user provided handler and call the user provided handler
     according to the command request received.
     :param device_client: The device client
     :param component_name: The name of the device like "sensor"
     :param method_name: (optional) The specific method name to listen for. Eg could be "blink", "turnon" etc.
     If not provided the listener will listen for all methods.
     :param user_command_handler: (optional) The user provided handler that needs to be executed after receiving "command requests".
     If not provided nothing will be executed on receiving command.
     :param create_user_response_handler: (optional) The user provided handler that will create a response.
     If not provided a generic response will be created.
     :return: ##################################################### # PROPERTY TASKS # blocking call ##################################################### # An # END KEYBOARD INPUT LISTENER to quit application Listener for quitting the sample # END KEYBOARD INPUT LISTENER ##################################################### ##################################################### # MAIN STARTS # Connect the client. ################################################ # Update readable properties from various components ################################################ # Get all the listeners running ################################################ # Function to send telemetry every 8 seconds #curr_temp_ext = random.randrange(10, 50) #THERMOSTAT_1.record(curr_temp_ext) ######################### #print('hello') #            print('Luminosity value = {luminosity}' .format(luminosity_t = luminosity_raw)) # how much has it changed since the last read? #    pot_adjust = abs(trim_pot - last_read) #    if pot_adjust > tolerance: #        trim_pot_changed = True #    if trim_pot_changed: # convert 16bit adc0 (0-65535) trim pot read into 0-100 volume level # set_volume = remap_range(trim_pot, 0, 65535, 0, 100) # set OS volume playback volume ####################### #temperature_msg1 = {"temperature": moisture} #await send_telemetry_from_temp_controller( #   device_client, temperature_msg1, thermostat_1_component_name #) #curr_temp_int = random.randrange(10, 50)  # Current temperature in Celsius #THERMOSTAT_2.record(curr_temp_int) # workingset_msg3 = {"workingSet": random.randrange(1, 100)} # await send_telemetry_from_temp_controller(device_client, workingset_msg3) # Run the stdin listener in the event loop # # Wait for user to indicate they are done listening for method calls # Finally, shut down the client ##################################################### # EXECUTE MAIN # If using Python 3.6 or below, use the following code instead of asyncio.run(main()): # loop = asyncio.get_event_loop() # loop.run_until_complete(main()) # loop.close()