#include <Arduino.h>
#include <WiFi.h>

#include "SinricPro.h"
#include "SinricProBlinds.h"

#define WIFI_SSID            "Wokwi-GUEST"          // don't change for Wokwi simulation!
#define WIFI_PASS            ""                     // don't change for Wokwi simulation
#define APP_KEY              "YOUR_APPKEY_HERE"     // Should look like "de0bxxxx-1x3x-4x3x-ax2x-5dabxxxxxxxx"
#define APP_SECRET           "YOUR_APPSECRET_HERE"  // Should look like "5f36xxxx-x3x7-4x3x-xexe-e86724a9xxxx-4c4axxxx-3x3x-x5xe-x9x3-333d65xxxxxx"
#define BLINDS_ID            "YOUR_DEVICE_ID_HERE"  // Should look like "5dc1564130xxxxxxxxxxxxxx"
#define BAUD_RATE            115200                 // Change baudrate to your need
#define FULL_DISTANCE_STEPS  1000                   // Number of steps required to open / close the blind completely
#define STEPS_PER_REVOLUTION 200                    // Steps per revolution - is used to calculate the speed
#define RPM                  60                     // Revolutions per minute
#define DIR_PIN              18
#define STEP_PIN             19

bool powerState = true;  // assume device is turned on

int currentPosition     = 0;  // assume blinds are closed
int destinationPosition = 0;

enum class StepperDirection {
    close = -1,
    idle  = 0,
    open  = 1
};

StepperDirection currentDirection = StepperDirection::idle;

// Converts a percentage into absolute steps.
int positionToSteps(int position) {
    return map(position, 0, 100, 0, FULL_DISTANCE_STEPS);
}

// Converts absolute steps into a percentage.
int stepsToPosition(int steps) {
    return map(steps, 0, FULL_DISTANCE_STEPS, 0, 100);
}

// performs a step in the current direction and updates currentPosition
void doStep() {
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(1);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(1);
    currentPosition += (int)currentDirection;
}

// set a direction
void setDirection(StepperDirection direction) {
    if (direction == StepperDirection::close) digitalWrite(DIR_PIN, HIGH);
    if (direction == StepperDirection::open) digitalWrite(DIR_PIN, LOW);
    currentDirection = direction;
}

// handles the stepper motor
void handleStepper() {
    if (currentDirection == StepperDirection::idle) return;
    if (!powerState) return;

    static unsigned long lastMillis;
    unsigned long        currentMillis = millis();
    unsigned long        millisToWait  = (60 * 1000) / (STEPS_PER_REVOLUTION * RPM);
    if (lastMillis && currentMillis - lastMillis < millisToWait) return;
    lastMillis = currentMillis;

    doStep();

    if (currentPosition == destinationPosition) setDirection(StepperDirection::idle);
}

void setupStepper() {
    pinMode(STEP_PIN, OUTPUT);
    pinMode(DIR_PIN, OUTPUT);
}

bool onPowerState(const String &deviceId, bool &state) {
    Serial.printf("Device %s power turned %s \r\n", deviceId.c_str(), state ? "on" : "off");
    powerState = state;
    if (!powerState) {
        destinationPosition = currentPosition;
        currentDirection         = StepperDirection::idle;
    }
    return true;  // request handled properly
}

// open = 100 / close = 0
bool onRangeValue(const String &deviceId, int &position) {
    if (!powerState) {
        Serial.println("Device is turned off. Please turn on first!");
        return true;
    }

    destinationPosition = positionToSteps(position);

    if (destinationPosition > currentPosition) {
        setDirection(StepperDirection::open);
    }

    if (destinationPosition < currentPosition) {
        setDirection(StepperDirection::close);
    }

    return true;  // request handled properly
}

void setupWiFi() {
    Serial.println("Connecting WiFi");
    WiFi.begin("Wokwi-GUEST", "");
    while (WiFi.status() != WL_CONNECTED) {
      Serial.print(".");
      delay(500);
    }
    Serial.println("connected!");
}

void setupSinricPro() {
    SinricProBlinds &myBlinds = SinricPro[BLINDS_ID];
    myBlinds.onPowerState(onPowerState);
    myBlinds.onRangeValue(onRangeValue);

    SinricPro.onConnected([]() { Serial.printf("Connected to SinricPro\r\n"); });
    SinricPro.onDisconnected([]() { Serial.printf("Disconnected from SinricPro\r\n"); });
    SinricPro.begin(APP_KEY, APP_SECRET);
    Serial.println("Connecting SinricPro...");
}

void setup() {
    Serial.begin(BAUD_RATE);
    setupStepper();
    setupWiFi();
    setupSinricPro();
}

void loop() {
    SinricPro.handle();
    handleStepper();
    delay(1);  // wokwi delay - remove in production!!
}
A4988