Git Product home page Git Product logo

hap-python's Issues

Persist char. values on accessory driver stop

Characteristic value updates are not persisted, thus, when HAP-python starts again, the last values are not seen in iOS. This should be done when the accessory driver stops (doing it on every update would be an overkill).

Pairing not initiating/completing.

I'm still hunting down exactly what's going on, but it seems that for some reason I can't successfully pair any HAP-python accessories any longer.

I'm not sure this is something that has changed with our code, as I un-paired and then attempted to re-pair with one that I know worked a couple of weeks ago, but without any luck.

If you check out the repo to master, and then run tox -e temperature, it should start a server, and show the QR code. In my case, I'm not able to finish the pairing process.

img_0283

I've turned logging up to DEBUG, and I don't see the pairing being initiated at all.

Accessory Object parameters

The issue regarding home-assistant/core#12488 (comment)

As I mentioned above, I personally don't like the way Hap-python handles the __init__ calls for Accessories. At the moment calls look something like this:

main.py

acc = TemperatureSensor(display_name='Temperatur')

TemperatureSensor(Accessory)

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)

Parameter are just being forwarded until Accessory. If you at a wrapper class in between, another level that just forwards the information is introduced.

In python their should be a better way to do this, although that might require some more remodeling. Unfortunately I woun't have time until the next weekend to take a deeper look at it.

Amended commits

Hi @gerswin,

I needed to amend the author info about all my past commits to HAP-python (no code changes). This may have affected your fork. If so, please accept my apologies and if you need any help in removing issues, please let me know.

Best,
Ivan

Enforce value constraints in Characteristic. Distinguish raw and HAP value.

Currently, Characteristic only respects min/max value constraints when sending its state through the to_HAP method. However, when a value change is published to iOS clients (notify), the sent value could be outside of min/max range. There should be a distinction between actual char value and it's "hap" representation, .e.g:

char = Characteristic(...)
char.value  # raw value
char.get_value()  # hapified value
char.set_value(value, ...)
# ^ should be equivalent to
if value not in self.allowed_values: raise ....
self.value = value

Race condition when setting timeout for send event; Document Characteristic.setter_callback

Hi,

I'm trying to set up a door (LockMechanism service) and I'm having this problem: the hap_server responds with timed out.

The door is initially closed and then I tap to open it within the apple's home app.

Here is what I have:

main.py

"""An example of how to setup and start an Accessory.

This is:
1. Create the Accessory object you want.
2. Add it to an AccessoryDriver, which will advertise it on the local network,
    setup a server to answer client queries, etc.
"""
import logging
import signal

from pyhap.accessories.ElectronicLock import ElectronicLock
from pyhap.accessory import Bridge
from pyhap.accessory_driver import AccessoryDriver

logging.basicConfig(level=logging.DEBUG)

def get_accessory():
    """Call this method to get a standalone Accessory."""
    acc = ElectronicLock("Door")
    return acc

acc = get_accessory()

# Start the accessory on port 51826
driver = AccessoryDriver(acc, port=51826)
# We want KeyboardInterrupts and SIGTERM (kill) to be handled by the driver itself,
# so that it can gracefully stop the accessory, server and advertising.
signal.signal(signal.SIGINT, driver.signal_handler)
signal.signal(signal.SIGTERM, driver.signal_handler)
# Start it!
driver.start()

ElectronicLock.py

from pyhap.accessory import Accessory, Category
import pyhap.loader as loader

class LOCK_STATE:
   OPEN = 0
   CLOSED = 1

class ElectronicLock(Accessory):
   category = Category.DOOR

   def __init__(self, *args, **kwargs):
      super(ElectronicLock, self).__init__(*args, **kwargs)

   def set_state(self, value):
      self.get_service("LockMechanism").get_characteristic("LockCurrentState").set_value(value)
      self.get_service("LockMechanism").get_characteristic("LockTargetState").set_value(value, should_callback=False)

   def lock_target_callback(self, value):
      if value == LOCK_STATE.OPEN:
         print("OPEN")
      else:
         print("CLOSED")
      self.set_state(value)

   def _set_services(self):
      super(ElectronicLock, self)._set_services()
      lm_service = loader.get_serv_loader().get("LockMechanism")
      self.add_service(lm_service)
      lm_service.get_characteristic("LockTargetState").setter_callback = self.lock_target_callback

terminal log:

DEBUG:pyhap.hap_server:Request PUT from address '('192.168.1.100', 54902)' for path '/characteristics'.
INFO:pyhap.hap_server:192.168.1.100 - "PUT /characteristics HTTP/1.1" 207 -
DEBUG:pyhap.hap_server:Request GET from address '('192.168.1.100', 54902)' for path '/characteristics?id=1.8,1.9'.
INFO:pyhap.hap_server:192.168.1.100 - "GET /characteristics?id=1.8,1.9 HTTP/1.1" 207 -
DEBUG:pyhap.hap_server:Request PUT from address '('192.168.1.100', 54902)' for path '/characteristics'.
OPEN
INFO:pyhap.hap_server:192.168.1.100 - "PUT /characteristics HTTP/1.1" 207 -
INFO:pyhap.hap_server:192.168.1.100 - Request timed out: timeout('timed out',)

The thing is that after the time out occurs, the server seems to stop listening new requests. I took a look at HAPServerHandler -> BaseHTTPRequestHandler but I couldn't find anything.

Any ideas why this is happening?

Pull latest Apple HAP types. Define overrides.

Apple has pushed new Services and Characteristics, which need to be parsed and added to HAP-python using gen_hap_types.py.

Also, Apple specifies some non-intuitive default characteristic constraints - for example, by default the CurrentTemperature has a min value of 0, i.e. you cannot report negative values. Thus, there should be a way to override Apple's default values at the end of gen_hap_types.py. See pull request #28

Make AccessoryEncoder easier to subclass.

Currently, it's really not possible to extend the encoder: you need to copy-paste the method body, and then add your extra stuff you want to store in the state before you dump it to json.

I think each operation should be split into two, so it's possible to add/change the data that will be persisted (and handle that extra data on the way in), but also more easily switch the encoding method (for instance, to be able to use a non-JSON encoder).

class Encoder(object):
    def prepare_state(self, accessory):
        return { ... }

    def persist(self, fp, accessory):
        json.dump(self.prepare_state(accessory), fp)

    def restore_state(self, accessory, state):
        ...

    def load_into(self, fp, accessory):
        self.restore_state(accessory, json.load(fp))

I'm not sure if those are the best names, but hopefully get the idea.

Generate QR Code (and X-HM:// url)

A nice feature about Homebridge/hap-nodejs is that you can scan a QR code in a terminal window.

I've had a quick look at the code for generating it, and if I can port it to Python I'll create a PR.

i'm making LightBulb with mqtt and need to get state of device

this is my code, can u explain me how to get the status of device.


import logging

from pyhap.accessory import Accessory, Category
import pyhap.loader as loader
import paho.mqtt.subscribe as subscribe
import paho.mqtt.publish as publish

auth = {
  'username':"",
  'password':""
}


class LightBulb(Accessory):

    category = Category.LIGHTBULB

    def __init__(self, *args,**kwargs):
        super(LightBulb, self).__init__(*args, **kwargs)

        


    def __setstate__(self, state):
        self.__dict__.update(state)
       

    def set_bulb(self, value):
        print(value) 
        publish.single("inTopic",
        payload=value,
        hostname="localhost",
        client_id="screen",
        auth=auth,
        retain=False,
        port=1883)

    def _set_services(self):
        super(LightBulb, self)._set_services()

        bulb_service = loader.get_serv_loader().get("Lightbulb")
        self.add_service(bulb_service)
        bulb_service.get_characteristic("On").setter_callback = self.set_bulb

    def stop(self):
        super(LightBulb, self).stop()
        

Garage Door Opener

Trying to get this working, and it works for the HomeKit requests, but not when the door state changes outside of HomeKit (i.e. when I open the garage with my car remote, instead of HomeKit).

Would like some help troubleshooting this. I am getting the opposite transition state than you would expect, only when the door state changes outside of a HomeKit request from an iOS device.

You can easily simulate this on a RPi, by shorting out a GPIO pin to ground for a CLOSED state, and removing from ground for an OPEN state. On the iOS, device you can request open, then simulate open, and so forth.

To repro the issue I am having, simply simulate the opposite state of the door, without requesting it from HAP-python.

Also, would like some clarification on how to properly set the initial state of CurrentDoorState, as I was seeing some weird things there too, like starting out as Opening, even though it was clearly set to CLOSED

Here is a simple driver, using pin 17 (BCM)/ 11(BOARD) for the switch, using the new QR stuff of course ;)

from pyhap.accessories.GarageDoor import GarageDoor
from pyhap.accessory_driver import AccessoryDriver
import signal


garage = GarageDoor(2, 17, 'GarageDoorTest', pincode=b'920-54-727')
print(garage.display_name)
print(garage.pincode.decode())
garage.print_qr()
driver = AccessoryDriver(garage, port=51826)
signal.signal(signal.SIGINT, driver.signal_handler)
signal.signal(signal.SIGTERM, driver.signal_handler)
driver.start()

Please see PR

Async support

Currently HAP-python uses threads which, at least on a larger scale, does impact performance quite a bit. Are there plans to change to an async framework, like asyncio in the future?

Unfortunately this would also require a more recent min python version, to take full advantage of the new async syntax introduced with py3.5.

Global_setter for accessories

I think it might be useful to implement something like a global_setter for services. Those should get called when one or more char values of this service are about to be changed to provide an access point for end users.

The concrete case is an issue / bug with the HomeKit lightbulb service. When setting the light to a specific brightness in the Home app from the off state, HomeKit calls to char client_update_values, one for the state (on / off) and one for the brightness char.
Reacting to both with the currently used setter_callbacks could lead to flickering if say the light is first set to on (100%) and only then to the wanted brightness.

Links:

Update
I've looked at the HAP doc, it might be better to implement it in the accessory class.

Allow characteristic properties like minValue to be overridden.

It should be possible to configure the properties of a characteristic to align with the functionality of the device and the iOS UI. For example, say your window covering can go up only to 50% open and that it is either closed or open. HAP specifies the default values for the coverings target position as maxValue=100 and minStep=0.1. This means iOS clients can slide at 0.1 step all the way to 100%, which you must map to all-up or all-down in your code.

Instead, you can do:

service = loader.get_serv_loader().get("WindowCovering")
pos = service.get_characteristic("TargetPosition")
pos.properties["maxValue"] = 50
pos.properties["minStep"] = 50
pos.hap_template = pos._create_hap_template()

Now iOS correctly allows you to only fully go up or down and the max display value is 50%. The hacky thing about the above is the last line and this is the topic of this issue - overriding properties should be more "native".

Way to add optional chars. to services in Http Accessory

Currently, you can use the Http accessory to expose non-hap-compatible devices by allowing them to submit characteristic updates in the form of http post requests.

However, you can only add services with their mandatory characteristics, whereas it should be possible to also add optional chars, such as Name.

This issue tracks the effort for the above.

Possible problem in iOS 11

Congratulations for your great work.

I add all accessory to homekit and I work perfectly but when I restart process, my iPhone with iOS 11.2.1 not reconnect accesories. The accessories have simbol (!). What can I do?

Thank you very much

Add deprecated chars and services

The current version removes deprecated Apple-defined characteristics and services. These should be available somewhere for people using an older iOS.

More Sphinx Work

Putting this issue here to track progress on the Sphinx Documentation.

Todo:

  • We will need to go through more of the project and switch over to sphinx docstrings. I only hit Accessory, Bridge, and AccessoryDriver.
  • Ubuntu LTS Installation Pre-Requisites.
  • More things, please comment for what you see missing.
  • intro/examples.rst
  • intro/tutorial.rst
  • intro/overview.rst

[Discussion] Alternative definition of accessories.

I don't really like the amount of work that I have to do to define an accessory, and use it - specifically: creating a driver, and how much super()... stuff is required.

Taking a leaf out of django's book, I've been playing around with using metaclasses, and a more declarative approach to declaring the accessory, and her services.

As a result, I have a syntax for definition that looks like:

class TemperatureSensor(Accessory):
    category = 'SENSOR'

    class AccessoryInformation:
        Manufacturer = 'Matthew Schinckel'
        Model = 'DS18B20'
        SerialNumber = '28-0516c0fc92ff'

    temperature = RemoteDS18B20(server='garagepi.local', sensor_id='28-0516c0fc92ff')

I'm not totally finished with the concept yet, as I think that maybe the Name field could be defined on the class like this too.

To use it, you can then do:

TemperatureSensor('Garage', port=XXX, persist_file=YYY).start()

There's lots more moving parts, but I think it's a cleaner syntax, but it moves lots of the logic from the accessory level down to the service level. I understand this may not be the way the whole project wants to go, and as such it's a wrapper around classes from HAP-python.

A link to the code is coming soon.

Support for HAP included services

Consider the following Apple-defined service in json as an example:

"LockMechanism": {
      "IncludedServices": [
         "00000044-0000-1000-8000-0026BB765291"
      ],
      "OptionalCharacteristics": [
         "Name"
      ],
      "RequiredCharacteristics": [
         "LockCurrentState",
         "LockTargetState"
      ],
      "UUID": "00000045-0000-1000-8000-0026BB765291"
 }

The IncludedServices field is not yet supported in HAP-python.

AccessoryService information.

I'm not exactly sure how to override the AccessoryService fields: I've tried a bit in my GarageDoor._set_services(), but it doesn't seem to work.

I wonder if there should be a way to have this information either pulled from the class, or some other mechanism.

Implement Characteristic.set_value callback toggle.

I have a large framework for a home automation system which in many ways is a bridge of bridges, acting as an overall compatibility and abstraction layer across multiple systems.

  • I can set the values of the lights for Homekit directly from my code using service.characteristic.set_value and this works no problem.
  • I need to be notified about lights change requests from a Homekit user so that I can reflect that on the actual device. Added a setter_callback which pushes the requested change to my code (which then applies it to the physical light) and this works no problem

However with both of these components in place, my code sets the value of the light, which triggers the callback, which calls my code, which updates the value of the light, which triggers the callback (repeat forever).

Is there a way for me to differentiate so that the callback is only triggered when a user makes a change and not when the device is updated from my code?

Dynamic pin code and setup IDs.

Reading through the Apple HAP documentation, it's pretty clear that "if a device can dynamically generate pin codes, it should."

To that end, I started playing around with stuff, and a dynamically generated pin code has no drawbacks (other than having to see what value was generated this time around).

I believe the same is true of setup id (which is used for the URL generation in #31 and #30). To that end, I believe that these values should not be stored in the pickle.

The other thing I discovered while developing accessories is that pickle is not a great way of storing stuff, especially during development. It means that if you change a class, or an import, then things can stop working, and you'd need to delete the pickle file and then re-add the accessory. There are also possible security implications of pickling data, but I'm less concerned about that because we aren't unpickling user supplied data.

Implement optional characteristics

Hello Ivan,

I did try to use optional characteristics for example "RotationSpeed" for "Fan" but this thing is not implemented, so I had to use https://github.com/KhaosT/HAP-NodeJS
But I prefer using Python instead of JS, would be nice to implement it and I can test it for you with my home-made Fan :-)

You can ask me for help :-)

Thanks,
Miroslav

self.broker.publish -> self.broker == None

Hi,

I am writing an adapter for HomeAssistant and I have been able to add a bridge, with a temperature and humidity sensors. Pairing goes fine but If I get an updated value from homeassistant I get an Error:

Traceback (most recent call last): File "/usr/lib/python3.5/concurrent/futures/thread.py", line 55, in run result = self.fn(*self.args, **self.kwargs) File "/home/homeassistant/.homeassistant/custom_components/HomeKit.py", line 105, in update_value self.hum_char.set_value(float(new_state.state)) File "/opt/homeassistant/lib/python3.5/site-packages/pyhap/characteristic.py", line 141, in set_value self.notify() File "/opt/homeassistant/lib/python3.5/site-packages/pyhap/characteristic.py", line 174, in notify self.broker.publish(data, self) File "/opt/homeassistant/lib/python3.5/site-packages/pyhap/accessory.py", line 328, in publish self.broker.publish(acc_data) AttributeError: 'NoneType' object has no attribute 'publish'

update_value is a class method of (in this case HumiditySensor, same for TemperatureSensor) which is called by HomeAssistant with the updated value.

my code for Humidity Sensor:

class HumiditySensor(Accessory):
    category = Category.SENSOR
    
    def __init__(self, *args, **kwargs):
        super(HumiditySensor, self).__init__(*args, **kwargs)
        self.hum_char = self.get_service("HumiditySensor").get_characteristic("CurrentRelativeHumidity")
#        self.hum_char.setter_callback = self.humidity_changed

#    def humidity_changed(self, value):
#        _LOGGER.error("Humidity Changed : " +str(value))


    def _set_services(self):
        super(HumiditySensor, self)._set_services()
        hum_sensor_service = loader.get_serv_loader().get("HumiditySensor")
        self.add_service(hum_sensor_service)

    def update_value(self,entity_id, old_state, new_state):
        self.hum_char.set_value(float(new_state.state))

bridge = False # points to the bridge object after init

def setup(hass,config):
   global bridge #Not sure If I need this...
    T = TemperatureSensor("Temperatuur ")
    H = HumiditySensor("Relatieve Luchtvochtigheid")
    _LOGGER.info("HomeKit bridge started ...")
    _LOGGER.info("display_name: "+config[DOMAIN]["display_name"])
    bridge = Bridge(display_name = config[DOMAIN]["display_name"],pincode=b"123-45-678")
    bridge.add_accessory(T)
    bridge.add_accessory(H)
    if os.path.exists("accessory.pickle"):
        with open("accessory.pickle","rb") as f:
            acc=pickle.load(f)
    else:
        acc=bridge
    driver = AccessoryDriver(acc, 51826, address="10.3.3.40")
    driver.start()

    def handle_homeassistant_stop(event):
        driver.stop()

    hass.bus.listen('homeasststant_stop', handle_homeassistant_stop)
    track_state_change(hass, "sensor.temperatuur_woonkamer_bmp", T.update_value)
    track_state_change(hass, "sensor.relatieve_luchtvochtigheid", H.update_value)

setup is called by HomeAssistant at startup

I have to set the tcpip address as I have a dual homed server. and driver.stop() is called when HomeAssistant is about to exit/stop and the last two lines are to track the update value events.

I'm puzzled where this error comes from, I must have forgotten something.

Any help on how to find out what is wrong would be highly appreciated!

Kind regards,

Martijn

Thread safety in set_value

@ikalchev do you have any docs covering thread safety? I know elaborated about how threads are used in #74, but it looks like characteristics set_value() is not really thread safe, while being called from multiple threads.

Also do you offer guidance anywhere for how an accessories run() loop should interact with requests from py-HAP in a thread safe manner?

Garage Door Opener

Thanks for sharing this project, I find python a little easier to using NodeJS. I can get the TempSensor, LightBulb to work on my Pi Zero W, but my attempt to mash code from multiple existing accessories to create a Garage Door Opener fails (it pairs and then unpairs right away). Can someone can point to the issues with this code? Not sure getstate is needed at all, my suspicion was that the required characteristic state is not getting set correctly.

class GarageDoorOpener(Accessory):
category = Category.GARAGE_DOOR_OPENER

@classmethod
def _gpio_setup(_cls, pulse_pin, sense_pin):
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(pulse_pin, GPIO.OUT)
    GPIO.setup(sense_pin, GPIO.IN)
    GPIO.add_event_detect(sense_pin, GPIO.BOTH, callback=self._detected)

def __init__(self, *args, pulse_pin=26, sense_pin=22, **kwargs):
    super(GarageDoorOpener, self).__init__(*args, **kwargs)

    self.CurrentDoorState = self.get_service("GarageDoorOpener").get_characteristic("CurrentDoorState")
    self.ObstructionDetected = self.get_service("GarageDoorOpener").get_characteristic("ObstructionDetected")

    self.pulse_pin = pulse_pin
    self.sense_pin = sense_pin
    self._gpio_setup(pulse_pin,sense_pin)

def __getstate__(self):
    state = super(GarageDoorOpener, self).__getstate__()
    return state

def __setstate__(self, state):
    self.__dict__.update(state)
    self._gpio_setup(self.pulse_pin, self.sense_pin)

def _pulse_door(self, value):
    GPIO.output(self.pulse_pin, GPIO.HIGH)
    time.sleep(2)
    GPIO.output(self.pulse_pin, GPIO.LOW)
    time.sleep(2)

def _set_services(self):
    super(GarageDoorOpener, self)._set_services()

    garage_door_opener_service = loader.get_serv_loader().get("GarageDoorOpener")
    self.add_service(garage_door_opener_service)
    garage_door_opener_service.get_characteristic("TargetDoorState").setter_callback = self._pulse_door

def _detected(self, _pin):
    if(GPIO.input(self.sense_pin)):
        self.CurrentDoorState.set_value(1)
    else:
        self.CurrentDoorState.set_value(0)

def stop(self):
    super(GarageDoorOpener, self).stop()
    GPIO.cleanup()

~

Accessory calculates AID incorrectly

The Accessory module calculates AID like this:

acc.aid = len(self.accessories) + 2

However if you have previously deleted and created a new AID, the max number of that AID is higher, so it should be:

if len(self.accessories)>0:
acc.aid = max(self.accessories) + 1
else:
acc.aid = 2

Suggestion: HAP-python-min

I was thinking if it would make sense to add a new pypi project: HAP-python-min. This would include all content from pyhap except for the accessories folder.

I'm currently working on a component for Home Assistant. Some of the examples form the accessories folder may work, but to port of all functions is basically more difficult than to write a new class which extends accessory. Because the initial code base for Home Assistant is already big their is a focus to keep additional imports as small as possible to improve performance. A min version would help accomplish that.

Remove accessory is not working properly

I found a small issue with removing accessory.
When I try to remove it I takes very long time sometimes iPhone get stuck and in console there is this error and accessory.pickle is not removed

screenshots:
screen shot 2017-11-29 at 19 39 16

before remove:
img_0937

after remove:
img_0938

I found that there is # TODO: can we have more than one connection? in my home I have AppleTV as HUB and another iPhone with I'm sharing my home.

Dont worry to ask me with some help :-)

Suggestion: Dev branch for repo

Maybe it could be a good idea to create a dev branch for this project.
All PR's would be against the dev branch and later merged with one PR to master.

This would allow for easier maintenance of other projects using HAP-Python with the added benefit of a rough change log for each new version in the master branch.

Sphinx Documentation

Wanted to open an issue to discuss this PR.

We should get some Sphinx (or some other auto-generated documentation) in place now that HA is starting to pick up interest. This will help a lot with new people.

Some basic stuff has been added, wanted to get feedback before I went too deep down the rabbit hole.
It's setup on the ReadTheDocs theme.

[Feature Request] getter callback

I noticed that in all the example accessories there is always some sort of polling loop updating a char value.

I think it would be good to add a getter_callback (in addition to the setter_callback) that is called when an iOS client requests a char value. Then the request can trigger a sensor poll, returning the most recent data, updating the local value, and sending any notifications.

This would also remove the need for an explicit polling loop in most cases.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.