import machine
import utime as time
from machine import Pin, PWM
import micropython

class Rotary:
    ROT_CW = 1
    ROT_CCW = 2
    SW_PRESS = 4
    SW_RELEASE = 8

    def __init__(self, dt, clk, sw):
        self.dt_pin = Pin(dt, Pin.IN, Pin.PULL_DOWN)
        self.clk_pin = Pin(clk, Pin.IN, Pin.PULL_DOWN)
        self.sw_pin = Pin(sw, Pin.IN, Pin.PULL_UP)
        self.last_status = (self.dt_pin.value() << 1) | self.clk_pin.value()
        self.dt_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING)
        self.clk_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING)
        self.sw_pin.irq(handler=self.switch_detect, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING)
        self.handlers = []
        self.last_button_status = self.sw_pin.value()

    def rotary_change(self, pin):
        new_status = (self.dt_pin.value() << 1) | self.clk_pin.value()
        if new_status == self.last_status:
            return
        transition = (self.last_status << 2) | new_status
        if transition == 0b1110:
            micropython.schedule(self.call_handlers, Rotary.ROT_CW)
        elif transition == 0b1101:
            micropython.schedule(self.call_handlers, Rotary.ROT_CCW)
        self.last_status = new_status

    def switch_detect(self, pin):
        if self.last_button_status == self.sw_pin.value():
            return
        self.last_button_status = self.sw_pin.value()
        if self.sw_pin.value():
            micropython.schedule(self.call_handlers, Rotary.SW_RELEASE)
        else:
            micropython.schedule(self.call_handlers, Rotary.SW_PRESS)

    def add_handler(self, handler):
        self.handlers.append(handler)

    def call_handlers(self, type):
        for handler in self.handlers:
            handler(type)

# Initialize components
pir_sensor = Pin(15, Pin.IN)  # PIR sensor connected to GPIO 15
servo1 = PWM(Pin(16))  # Servo 1 connected to GPIO 16
servo2 = PWM(Pin(17))  # Servo 2 connected to GPIO 17

# Set PWM frequency for servos
servo1.freq(50)
servo2.freq(50)

# Function to move servos
def set_servo_angle(servo, angle):
    duty = int((angle / 180.0 * 2 + 0.5) * 1023 / 20)  # Convert angle to duty cycle
    servo.duty_u16(duty)

# Rotary encoder initialization
rotary = Rotary(18, 19, 20)  # GPIO 18 = DT, GPIO 19 = CLK, GPIO 20 = SW

# Handler for rotary encoder
def rotary_handler(event):
    global door_speed
    if event == Rotary.ROT_CW:
        print("Rotary CW: Increasing speed")
        door_speed = min(door_speed + 1, 10)  # Increase speed
    elif event == Rotary.ROT_CCW:
        print("Rotary CCW: Decreasing speed")
        door_speed = max(door_speed - 1, 1)  # Decrease speed
    elif event == Rotary.SW_PRESS:
        print("Rotary Button Pressed")
    elif event == Rotary.SW_RELEASE:
        print("Rotary Button Released")

rotary.add_handler(rotary_handler)

# Default speed for door movement
door_speed = 5

# Main loop
while True:
    if pir_sensor.value():  # Motion detected
        print("Motion detected! Opening door...")
        set_servo_angle(servo1, 90)  # Open door
        set_servo_angle(servo2, 90)
        time.sleep(door_speed / 10)  # Adjust speed based on rotary encoder
    else:  # No motion detected
        print("No motion detected. Closing door...")
        set_servo_angle(servo1, 0)  # Close door
        set_servo_angle(servo2, 180)
        time.sleep(door_speed / 10)  # Adjust speed based on rotary encoder