|
| 1 | +"""This virtual lightbulb implements the bare minimum needed for HomeKit |
| 2 | + controller to recognize it as having AdaptiveLightning |
| 3 | +""" |
| 4 | +import logging |
| 5 | +import signal |
| 6 | +import random |
| 7 | +import tlv8 |
| 8 | +import base64 |
| 9 | + |
| 10 | +from pyhap.accessory import Accessory |
| 11 | +from pyhap.accessory_driver import AccessoryDriver |
| 12 | +from pyhap.const import (CATEGORY_LIGHTBULB, |
| 13 | + HAP_REPR_IID) |
| 14 | +from pyhap.loader import get_loader |
| 15 | + |
| 16 | +# Define tlv8 Keys and Values |
| 17 | +SUPPORTED_TRANSITION_CONFIGURATION = 1 |
| 18 | +CHARACTERISTIC_IID = 1 |
| 19 | +TRANSITION_TYPE = 2 |
| 20 | + |
| 21 | +BRIGHTNESS = 1 |
| 22 | +COLOR_TEMPERATURE = 2 |
| 23 | + |
| 24 | +logging.basicConfig(level=logging.DEBUG, format="[%(module)s] %(message)s") |
| 25 | + |
| 26 | +def bytes_to_base64_string(value: bytes) -> str: |
| 27 | + return base64.b64encode(value).decode('ASCII') |
| 28 | + |
| 29 | +class LightBulb(Accessory): |
| 30 | + """Fake lightbulb, logs what the client sets.""" |
| 31 | + |
| 32 | + category = CATEGORY_LIGHTBULB |
| 33 | + |
| 34 | + def __init__(self, *args, **kwargs): |
| 35 | + super().__init__(*args, **kwargs) |
| 36 | + |
| 37 | + serv_light = self.add_preload_service('Lightbulb', [ |
| 38 | + # The names here refer to the Characteristic name defined |
| 39 | + # in characteristic.json |
| 40 | + "Brightness", |
| 41 | + "ColorTemperature", |
| 42 | + "ActiveTransitionCount", |
| 43 | + "TransitionControl", |
| 44 | + "SupportedTransitionConfiguration"]) |
| 45 | + |
| 46 | + self.char_on = serv_light.configure_char( |
| 47 | + 'On', setter_callback=self.set_on) |
| 48 | + self.char_br = serv_light.configure_char( |
| 49 | + 'Brightness', setter_callback=self.set_brightness) |
| 50 | + self.char_ct = serv_light.configure_char( |
| 51 | + 'ColorTemperature', setter_callback=self.set_ct, value=140) |
| 52 | + |
| 53 | + # Via this structure we advertise to the controller that we are |
| 54 | + # capable of autonomous transitions between states on brightness |
| 55 | + # and color temperature. |
| 56 | + supported_transitions = [tlv8.Entry(SUPPORTED_TRANSITION_CONFIGURATION, [ |
| 57 | + tlv8.Entry(CHARACTERISTIC_IID, self.char_br.to_HAP()[HAP_REPR_IID]), |
| 58 | + tlv8.Entry(TRANSITION_TYPE, BRIGHTNESS), |
| 59 | + tlv8.Entry(CHARACTERISTIC_IID, self.char_ct.to_HAP()[HAP_REPR_IID]), |
| 60 | + tlv8.Entry(TRANSITION_TYPE, COLOR_TEMPERATURE) |
| 61 | + ])] |
| 62 | + |
| 63 | + bytes_data = tlv8.encode(supported_transitions) |
| 64 | + b64str = bytes_to_base64_string(bytes_data) |
| 65 | + |
| 66 | + self.char_atc = serv_light.configure_char( |
| 67 | + 'ActiveTransitionCount', setter_callback=self.set_atc) |
| 68 | + self.char_tc = serv_light.configure_char( |
| 69 | + 'TransitionControl', setter_callback=self.set_tc) |
| 70 | + self.char_stc = serv_light.configure_char( |
| 71 | + 'SupportedTransitionConfiguration', |
| 72 | + value=b64str) |
| 73 | + |
| 74 | + def set_on(self, value): |
| 75 | + logging.info("Write On State: %s", value) |
| 76 | + |
| 77 | + def set_ct(self, value): |
| 78 | + logging.info("Bulb color temp: %s", value) |
| 79 | + |
| 80 | + def set_atc(self, value): |
| 81 | + logging.info("Write to ActiveTransactionCount: %s", value) |
| 82 | + |
| 83 | + def set_tc(self, value): |
| 84 | + logging.info("Write to TransitionControl: %s", value) |
| 85 | + |
| 86 | + def set_brightness(self, value): |
| 87 | + logging.info("Bulb brightness: %s", value) |
| 88 | + |
| 89 | +driver = AccessoryDriver(port=51826, persist_file='adaptive_lightbulb.state') |
| 90 | +driver.add_accessory(accessory=LightBulb(driver, 'Lightbulb')) |
| 91 | +signal.signal(signal.SIGTERM, driver.signal_handler) |
| 92 | +driver.start() |
| 93 | + |
0 commit comments