jbyerline / flask-purple-powerbase

MIT License
0 stars 0 forks source link

Missing Tilt/Lumbar and preset settings #1

Open tknmncr opened 2 years ago

tknmncr commented 2 years ago

I have a Reverie R650 bed frame, and it has three settings for position; head and feet, but also has a tilt function. The same function maps to lumbar position on other versions of their frame. Their own app is useless to me, because it assumes the lumbar values, so with I say "flat" in their app, the belt tilts to the extreme. They also do not answer support questions (not even with canned answers; it's just /dev/null to try and contact them). This tool you've created fits the bill for exactly what I wanted to do (i.e. integrate with homekit), but without the tilt function, I can't set it to my usual sleeping position. The bed also has 2 presets in addition to the other controls; they would also be nice to be able to call.

If you can let me know how you got the UUIDs of the bed functions, I can attempt to uncover them myself, and I'll send them to you if I can get them to work. I am assuming you used a bluetooth sniffer of some kind to suss out the UUIDs?

jbyerline commented 2 years ago

I am happy to hear that this plugin is of use to others. I would be happy to implement more functionality into this plugin. Have you already loaded it onto a Pi and tested it out?

So I used this bluetooth sniffing app on my iPhone, https://apps.apple.com/us/app/ble-scanner-4-0/id1221763603

I discovered the bed and connected to it within the app. One thing to note is that only one device can connect to the bed at a time. So you will have to disconnect your phone app or RPI or anything else that is connected to the bed over bluetooth before the app will see the bed. Once you connect you sill see a bunch of UUID services. Some of them will probably be similar to those in my code. You can click on them and write hexadecimal values to the bed. Usually they are numbers 0-100 but in hex. This should cause the bed to do something.

Also the preset buttons are just the normal uuid service codes that are hardcoded into the remote. So for example, I have a "No Snore" mode on my remote that slightly raises the head and foot. The remote just calls the raise head command and then the raise foot command and supplies them with a predetermined hex value. So there is no special service UUID for that preset.

So once you're done guessing and you've found the correct UUIDs you'd like to add, you're more than welcome to modify this code and put up a PR, or fork the code and create your own repo. If I have time and you send me the relevant info, I wouldn't mind adding it into this plugin.

I hope this helps!

tknmncr commented 2 years ago

Thanks! I’ll play with it and see what I can do with it. I’ll send you anything I manage to get working.

-- Scott Garrett @.*** http://www.technomancer.com

If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization.

On Nov 24, 2021, at 3:21 AM, Jacob Byerline @.***> wrote:

I am happy to hear that this plugin is of use to others. I would be happy to implement more functionality into this plugin. Have you already loaded it onto a Pi and tested it out?

So I used this bluetooth sniffing app on my iPhone, https://apps.apple.com/us/app/ble-scanner-4-0/id1221763603 https://apps.apple.com/us/app/ble-scanner-4-0/id1221763603 I discovered the bed and connected to it within the app. One thing to note is that only one device can connect to the bed at a time. So you will have to disconnect your phone app or RPI or anything else that is connected to the bed over bluetooth before the app will see the bed. Once you connect you sill see a bunch of UUID services. Some of them will probably be similar to those in my code. You can click on them and write hexadecimal values to the bed. Usually they are numbers 0-100 but in hex. This should cause the bed to do something.

Also the preset buttons are just the normal uuid service codes that are hardcoded into the remote. So for example, I have a "No Snore" mode on my remote that slightly raises the head and foot. The remote just calls the raise head command and then the raise foot command and supplies them with a predetermined hex value. So there is no special service UUID for that preset.

So once you're done guessing and you've found the correct UUIDs you'd like to add, you're more than welcome to modify this code and put up a PR, or fork the code and create your own repo. If I have time and you send me the relevant info, I wouldn't mind adding it into this plugin.

I hope this helps!

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/jbyerline/flask-purple-powerbase/issues/1#issuecomment-977638124, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC3FBLGQ53FLUPMRMXJ6PO3UNSN7XANCNFSM5IVAGOWQ. Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

tknmncr commented 2 years ago

I found the UUID for tilt/lumbar:

Tilt/Lumbar: UUID: db801040-f324-29c3-38d1-85c0c2e86885 Tilt: Values: 0x00(head all the way down) - 0x64(head all the way up) : 0x24(flat) Lumbar: Values: 0x00(flat) - 0x64(up)

My bed has tilt, and its range, like most of the settings is 0x00-0x64 (decimal -100 - 100). With their iPhone app, it seems to assume that I have lumbar support rather than tilt (with no way to tell it otherwise). But I can infer from that that 0x00 is flat for lumbar (because when I hit “flat” on the app. it tilts my head all the way down.

I set the bed to flat with the remote, and the value is 0x24. So I guess the ideal way to set it would be a variable in app.py to tell it whether you are using lumbar or tilt, and have it adjust accordingly.

I’m not a python person, but it’s easy enough to figure out most of the code. What I am not sure of is in your UUID dict, the second number; e.g. “lower lift” is “6”. I don’t see any corresponding value or even pattern in the scanner app that matches that number. Where does that value come from?

Oh, also, I made a systemd unit file for starting this service that allows it to use the native startup services rather than the rc.local method (it will also restart itself on failure):

I created a file called "flask-purple-powerbase.service” and put it in the same directory as app.py. Then (as root):

ln -s ~/src/flask-purple-powerbase/flask-purple-powerbase.service /etc/systemd/system/flask-purple-powerbase.service ; touch /etc/default/flask-purple-powerbase

The file /etc/default/flask-purple-powerbase is a file where you can put any environment you may want (like, say, the settings for whether it’s tilt or lumbar). The file needs to exist, even if it’s empty or the service won’t load.

Once you’ve done this, do “sudo systemctl enable flask-purple-powerbase.service” and then “sudo systemctl start flask-purple-powerbase.service” and the process should be running. If it dies, it will restart in 3 seconds.

The service definition is:

[Unit] Description=Purple Flask Powerbase Wants=network-online.target After=syslog.target network-online.target

[Service] Type=simple User=root WorkingDirectory=/home/pi/src/flask-purple-powerbase/ EnvironmentFile=/etc/default/flask-purple-powerbase ExecStart=/usr/bin/python3 /home/pi/src/flask-purple-powerbase/app.py >> /var/log/flask-purple-powerbase 2>&1 Restart=always RestartSec=3 KillMode=process

[Install] WantedBy=multi-user.target

-- Scott Garrett @.*** http://www.technomancer.com

"syn! .. syn|ack! .. ack!" - the mating call of the internet.

On 24Nov, 2021, at 03:21, Jacob Byerline @.***> wrote:

I am happy to hear that this plugin is of use to others. I would be happy to implement more functionality into this plugin. Have you already loaded it onto a Pi and tested it out?

So I used this bluetooth sniffing app on my iPhone, https://apps.apple.com/us/app/ble-scanner-4-0/id1221763603 https://apps.apple.com/us/app/ble-scanner-4-0/id1221763603 I discovered the bed and connected to it within the app. One thing to note is that only one device can connect to the bed at a time. So you will have to disconnect your phone app or RPI or anything else that is connected to the bed over bluetooth before the app will see the bed. Once you connect you sill see a bunch of UUID services. Some of them will probably be similar to those in my code. You can click on them and write hexadecimal values to the bed. Usually they are numbers 0-100 but in hex. This should cause the bed to do something.

Also the preset buttons are just the normal uuid service codes that are hardcoded into the remote. So for example, I have a "No Snore" mode on my remote that slightly raises the head and foot. The remote just calls the raise head command and then the raise foot command and supplies them with a predetermined hex value. So there is no special service UUID for that preset.

So once you're done guessing and you've found the correct UUIDs you'd like to add, you're more than welcome to modify this code and put up a PR, or fork the code and create your own repo. If I have time and you send me the relevant info, I wouldn't mind adding it into this plugin.

I hope this helps!

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/jbyerline/flask-purple-powerbase/issues/1#issuecomment-977638124, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC3FBLGQ53FLUPMRMXJ6PO3UNSN7XANCNFSM5IVAGOWQ. Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

jbyerline commented 2 years ago

Excellent, I am glad you got this figured out. If I remember correctly, when the "btle" instance which is named "dev" connects to the bed, the bed returns a list of available services to call. This is named "service". Then when we want to perform a particular action, we pick the sub-service we want by looking inside "service" for the right UUID. But these are referenced by index because "service" is a dictionary. So we pass in the index of the "subService" we want to perform. Once we have got that action queued up, we can then write some hexidecimal to it.

So you can probably print out "service" or "service.getCharacteristics()" and it should show you all the available services by uuid and their corresponding index.

As far as implementing this into the repo, I would like the code to be separated based on the model of bed. I have the Purple Powerbase made by Reverie, so we could add a constant called bed model at the top and basically say if(purple) use dictionary A. Else if (Reverie Model ABCD) use dictionary B

I like the idea of making it a service. When I have some time I will add that to the repo unless you beat me to it with a PR.

tknmncr commented 2 years ago

Ok, I have it mostly working now. The main problem is that if you send a command to move, say, the head and then send a command to move the feet, it stops the head moving, and begins moving the feet. Which means you can’t set scenes to move the bed to a particular setting of all the adjustments.

Did you ever figure out a workaround for that? Obviously it CAN be done, as the remote control sends a signal that moves all three at the same time. I’m still playing with trying to find the right combination of commands, but thought I’d save myself some headache if you happened to know.

For the app.py, here are the diffs of what I changed. Some of it was to add the tilt function, some to rename the “zero G” function to “position” to be more consistent with what it’s actually doing.

Below that, I added the diff for the code for the index.js of the homebridge plugin that makes it appear. I am still looking for a cleaner way to represent the tilt, as “flat” is 0x24 (or 36%). But for now, it does work, so long as you move each segment one at a time and let them complete before moving the next.

I’ll keep tinkering and see if I can get things working more smoothly. I’m going to attempt to talk to Reverie and see if they will provide API documentation for the controller. They don’t have an e-mail address listed on their site, and I’m sure whoever I talk to on the phone is going to have no clue what I’m talking about, but I can at least try… :)

——————————————————————————————

1,2d0 < #!/usr/bin/python3 < 16d13 < "tilt": 4, # db801040-f324-29c3-38d1-85c0c2e86885 22c19 < "position": [5, 6, 4], # db801041-f324-29c3-38d1-85c0c2e86885, db801042-f324-29c3-38d1-85c0c2e86885, db801040-f324-29c3-38d1-85c0c2e86885

"zero g": [5, 6],  # db801041-f324-29c3-38d1-85c0c2e86885, db801042-f324-29c3-38d1-85c0c2e86885

44,45c41,42 < subService = service.getCharacteristics()[UUID_DICT["lower vib"]] < subService.write(bytes.fromhex("00"))

subService1 = service.getCharacteristics()[UUID_DICT["lower vib"]]
subService1.write(bytes.fromhex("00"))

47,48c44,45 < subService = service.getCharacteristics()[UUID_DICT["upper vib"]] < subService.write(bytes.fromhex("00"))

subService2 = service.getCharacteristics()[UUID_DICT["upper vib"]]
subService2.write(bytes.fromhex("00"))

54,60c51,52 < subServiceUpper = service.getCharacteristics()[UUID_DICT["position"][0]] < subServiceLower = service.getCharacteristics()[UUID_DICT["position"][1]] < subServiceTilt = service.getCharacteristics()[UUID_DICT["position"][2]] < < subServiceUpper.write(bytes.fromhex("00")) < time.sleep(22) < subServiceLower.write(bytes.fromhex("00"))

subService1 = service.getCharacteristics()[UUID_DICT["zero g"][1]]
subService1.write(bytes.fromhex("00"))

62,64c54,55 < subServiceTilt.write(bytes.fromhex("24")) < time.sleep(22) <

subService2 = service.getCharacteristics()[UUID_DICT["zero g"][0]]
subService2.write(bytes.fromhex("00"))

70,80c61,65 < subServiceUpper = service.getCharacteristics()[UUID_DICT["position"][0]] < subServiceLower = service.getCharacteristics()[UUID_DICT["position"][1]] < subServiceTilt = service.getCharacteristics()[UUID_DICT["position"][2]] < < subServiceUpper.write(bytes.fromhex("1f")) < time.sleep(22) < subServiceLower.write(bytes.fromhex("46")) < time.sleep(22) < subServiceTilt.write(bytes.fromhex("24")) < time.sleep(22) <

subService1 = service.getCharacteristics()[UUID_DICT["zero g"][1]]
subService1.write(bytes.fromhex("1f"))
time.sleep(1)
subService2 = service.getCharacteristics()[UUID_DICT["zero g"][0]]
subService2.write(bytes.fromhex("46"))

86,87c71,72 < subService = service.getCharacteristics()[UUID_DICT["no snore"]] < subService.write(bytes.fromhex("0b"))

subService1 = service.getCharacteristics()[UUID_DICT["no snore"]]
subService1.write(bytes.fromhex("0b"))

93c78 < subService = service.getCharacteristics()[UUID_DICT["upper lift"]]

subService1 = service.getCharacteristics()[UUID_DICT["upper lift"]]

97c82 < subService.write(bytes.fromhex(hexval))

subService1.write(bytes.fromhex(hexval))

103,104c88,89 < subService = service.getCharacteristics()[UUID_DICT["upper lift"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder)

subService1 = service.getCharacteristics()[UUID_DICT["upper lift"]]
decval = int.from_bytes(subService1.read(), byteorder=sys.byteorder)

110c95 < subService = service.getCharacteristics()[UUID_DICT["lower lift"]]

subService1 = service.getCharacteristics()[UUID_DICT["lower lift"]]

114c99 < subService.write(bytes.fromhex(hexval))

subService1.write(bytes.fromhex(hexval))

120,121c105,106 < subService = service.getCharacteristics()[UUID_DICT["lower lift"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder)

subService1 = service.getCharacteristics()[UUID_DICT["lower lift"]]
decval = int.from_bytes(subService1.read(), byteorder=sys.byteorder)

124,137d108 < @app.route("/moveTilt/") < def moveTilt(percentage): < subService = service.getCharacteristics()[UUID_DICT["tilt"]] < hexval = hex(int(percentage))[2:] < if len(hexval) == 1: < hexval = "0" + hexval < subService.write(bytes.fromhex(hexval)) < return 'tilt' < < @app.route("/getTilt") < def getTilt(): < subService = service.getCharacteristics()[UUID_DICT["tilt"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder) < return str(decval) 141c112 < subService = service.getCharacteristics()[UUID_DICT["upper vib"]]

subService1 = service.getCharacteristics()[UUID_DICT["upper vib"]]

145c116 < subService.write(bytes.fromhex(hexval))

subService1.write(bytes.fromhex(hexval))

151,152c122,123 < subService = service.getCharacteristics()[UUID_DICT["upper vib"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder)

subService1 = service.getCharacteristics()[UUID_DICT["upper vib"]]
decval = int.from_bytes(subService1.read(), byteorder=sys.byteorder)

158c129 < subService = service.getCharacteristics()[UUID_DICT["lower vib"]]

subService1 = service.getCharacteristics()[UUID_DICT["lower vib"]]

162c133 < subService.write(bytes.fromhex(hexval))

subService1.write(bytes.fromhex(hexval))

168,169c139,140 < subService = service.getCharacteristics()[UUID_DICT["lower vib"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder)

subService1 = service.getCharacteristics()[UUID_DICT["lower vib"]]
decval = int.from_bytes(subService1.read(), byteorder=sys.byteorder)

188,189c159,160 < subService = service.getCharacteristics()[UUID_DICT["light"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder)

subService1 = service.getCharacteristics()[UUID_DICT["light"]]
decval = int.from_bytes(subService1.read(), byteorder=sys.byteorder)

——————————————————————————————

22,47d21 < getTilt: function (callback) { < request(this.baseUrl + "/getTilt", (err, resp, body) => { < if(err){ < this.log(err) < } < callback(null, parseInt(body)) < }); < }, < setTilt: function (value, callback) { < this.log('setting upper height ', value) < request(this.baseUrl + "/moveTilt/" + value, (err, resp, body) => { < callback(null, value) < }) < }, < getTiltStatus: function (callback) { < request(this.baseUrl + "/getTilt", (err, resp, body) => { < if(body == 0){ < this.isOn = false < } else { < this.log() < this.isOn = true < } < callback(null, this.isOn) < }) < }, < 210d183 < var tiltService = new Service.Lightbulb("Tilt Bed", "Tilt Bed"); 217,222d189 < tiltService.getCharacteristic(Characteristic.On) < .on('get', this.getTiltStatus.bind(this)) < tiltService.getCharacteristic(Characteristic.Brightness) < .on('get', this.getTilt.bind(this)) < .on('set', this.setTilt.bind(this)); < 260d226 < this.services.push(tiltService);

-- Scott Garrett @.*** http://www.technomancer.com

Some people are like slinkies - not really good for much of anything, but still bring a smile to your face when you push them down a flight *** of stairs.

On 28Nov, 2021, at 16:15, Jacob Byerline @.***> wrote:

Excellent, I am glad you got this figured out. If I remember correctly, when the "btle" instance which is named "dev" connects to the bed, the bed returns a list of available services to call. This is named "service". Then when we want to perform a particular action, we pick the sub-service we want by looking inside "service" for the right UUID. But these are referenced by index because "service" is a dictionary. So we pass in the index of the "subService" we want to perform. Once we have got that action queued up, we can then write some hexidecimal to it.

So you can probably print out "service" or "service.getCharacteristics()" and it should show you all the available services by uuid and their corresponding index.

As far as implementing this into the repo, I would like the code to be separated based on the model of bed. I have the Purple Powerbase made by Reverie, so we could add a constant called bed model at the top and basically say if(purple) use dictionary A. Else if (Reverie Model ABCD) use dictionary B

I like the idea of making it a service. When I have some time I will add that to the repo unless you beat me to it with a PR.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android.

tknmncr commented 2 years ago

I sort of took your code and ran with it… I’ve added more functions, expanded command set to the bed, put in a scanner to automatically find the bed, added some error checking, and documentation.

I’m sure it’ll still be a work in progress, but I thought you might benefit from what I’ve done. I did, however rename the functions so it won’t drop in with your HomeKit plugin.

My next goal is to rework that using different methods than light dimmers (maybe window controllers?). The timeouts on the dimmer are so short, there’s no way to use the function I added to wait until the bed reaches its position before releasing. That just causes homekit to freak out. Also, I’ve noticed that if you drag the sliders, it floods the python app with a LOT of requests, and sometimes they overrun each other. I did some work to alleviate that, but it’s not perfect.

I created a separate project on github; feel free to lift whatever I did an put it in your codebase if you find it useful:

https://github.com/tknmncr/reverie-powerbase/blob/main/reverie.py

-- Scott Garrett @.*** http://www.technomancer.com

"Thought is the brain's orgasm. Those who CAN experience it, enjoy it to the fullest. Others have to fake it."

On Nov 28, 2021, at 8:27 PM, Scott Garrett @.***> wrote:

Ok, I have it mostly working now. The main problem is that if you send a command to move, say, the head and then send a command to move the feet, it stops the head moving, and begins moving the feet. Which means you can’t set scenes to move the bed to a particular setting of all the adjustments.

Did you ever figure out a workaround for that? Obviously it CAN be done, as the remote control sends a signal that moves all three at the same time. I’m still playing with trying to find the right combination of commands, but thought I’d save myself some headache if you happened to know.

For the app.py, here are the diffs of what I changed. Some of it was to add the tilt function, some to rename the “zero G” function to “position” to be more consistent with what it’s actually doing.

Below that, I added the diff for the code for the index.js of the homebridge plugin that makes it appear. I am still looking for a cleaner way to represent the tilt, as “flat” is 0x24 (or 36%). But for now, it does work, so long as you move each segment one at a time and let them complete before moving the next.

I’ll keep tinkering and see if I can get things working more smoothly. I’m going to attempt to talk to Reverie and see if they will provide API documentation for the controller. They don’t have an e-mail address listed on their site, and I’m sure whoever I talk to on the phone is going to have no clue what I’m talking about, but I can at least try… :)

——————————————————————————————

1,2d0 < #!/usr/bin/python3 < 16d13 < "tilt": 4, # db801040-f324-29c3-38d1-85c0c2e86885 22c19 < "position": [5, 6, 4], # db801041-f324-29c3-38d1-85c0c2e86885, db801042-f324-29c3-38d1-85c0c2e86885, db801040-f324-29c3-38d1-85c0c2e86885

"zero g": [5, 6], # db801041-f324-29c3-38d1-85c0c2e86885, db801042-f324-29c3-38d1-85c0c2e86885 44,45c41,42 < subService = service.getCharacteristics()[UUID_DICT["lower vib"]] < subService.write(bytes.fromhex("00"))

subService1 = service.getCharacteristics()[UUID_DICT["lower vib"]] subService1.write(bytes.fromhex("00")) 47,48c44,45 < subService = service.getCharacteristics()[UUID_DICT["upper vib"]] < subService.write(bytes.fromhex("00"))

subService2 = service.getCharacteristics()[UUID_DICT["upper vib"]] subService2.write(bytes.fromhex("00")) 54,60c51,52 < subServiceUpper = service.getCharacteristics()[UUID_DICT["position"][0]] < subServiceLower = service.getCharacteristics()[UUID_DICT["position"][1]] < subServiceTilt = service.getCharacteristics()[UUID_DICT["position"][2]] < < subServiceUpper.write(bytes.fromhex("00")) < time.sleep(22) < subServiceLower.write(bytes.fromhex("00"))

subService1 = service.getCharacteristics()[UUID_DICT["zero g"][1]] subService1.write(bytes.fromhex("00")) 62,64c54,55 < subServiceTilt.write(bytes.fromhex("24")) < time.sleep(22) <

subService2 = service.getCharacteristics()[UUID_DICT["zero g"][0]] subService2.write(bytes.fromhex("00")) 70,80c61,65 < subServiceUpper = service.getCharacteristics()[UUID_DICT["position"][0]] < subServiceLower = service.getCharacteristics()[UUID_DICT["position"][1]] < subServiceTilt = service.getCharacteristics()[UUID_DICT["position"][2]] < < subServiceUpper.write(bytes.fromhex("1f")) < time.sleep(22) < subServiceLower.write(bytes.fromhex("46")) < time.sleep(22) < subServiceTilt.write(bytes.fromhex("24")) < time.sleep(22) <

subService1 = service.getCharacteristics()[UUID_DICT["zero g"][1]] subService1.write(bytes.fromhex("1f")) time.sleep(1) subService2 = service.getCharacteristics()[UUID_DICT["zero g"][0]] subService2.write(bytes.fromhex("46")) 86,87c71,72 < subService = service.getCharacteristics()[UUID_DICT["no snore"]] < subService.write(bytes.fromhex("0b"))

subService1 = service.getCharacteristics()[UUID_DICT["no snore"]] subService1.write(bytes.fromhex("0b")) 93c78 < subService = service.getCharacteristics()[UUID_DICT["upper lift"]]

subService1 = service.getCharacteristics()[UUID_DICT["upper lift"]] 97c82 < subService.write(bytes.fromhex(hexval))

subService1.write(bytes.fromhex(hexval)) 103,104c88,89 < subService = service.getCharacteristics()[UUID_DICT["upper lift"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder)

subService1 = service.getCharacteristics()[UUID_DICT["upper lift"]] decval = int.from_bytes(subService1.read(), byteorder=sys.byteorder) 110c95 < subService = service.getCharacteristics()[UUID_DICT["lower lift"]]

subService1 = service.getCharacteristics()[UUID_DICT["lower lift"]] 114c99 < subService.write(bytes.fromhex(hexval))

subService1.write(bytes.fromhex(hexval)) 120,121c105,106 < subService = service.getCharacteristics()[UUID_DICT["lower lift"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder)

subService1 = service.getCharacteristics()[UUID_DICT["lower lift"]] decval = int.from_bytes(subService1.read(), byteorder=sys.byteorder) 124,137d108 < @app.route("/moveTilt/") < def moveTilt(percentage): < subService = service.getCharacteristics()[UUID_DICT["tilt"]] < hexval = hex(int(percentage))[2:] < if len(hexval) == 1: < hexval = "0" + hexval < subService.write(bytes.fromhex(hexval)) < return 'tilt' < < @app.route("/getTilt") < def getTilt(): < subService = service.getCharacteristics()[UUID_DICT["tilt"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder) < return str(decval) 141c112 < subService = service.getCharacteristics()[UUID_DICT["upper vib"]]

subService1 = service.getCharacteristics()[UUID_DICT["upper vib"]] 145c116 < subService.write(bytes.fromhex(hexval))

subService1.write(bytes.fromhex(hexval)) 151,152c122,123 < subService = service.getCharacteristics()[UUID_DICT["upper vib"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder)

subService1 = service.getCharacteristics()[UUID_DICT["upper vib"]] decval = int.from_bytes(subService1.read(), byteorder=sys.byteorder) 158c129 < subService = service.getCharacteristics()[UUID_DICT["lower vib"]]

subService1 = service.getCharacteristics()[UUID_DICT["lower vib"]] 162c133 < subService.write(bytes.fromhex(hexval))

subService1.write(bytes.fromhex(hexval)) 168,169c139,140 < subService = service.getCharacteristics()[UUID_DICT["lower vib"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder)

subService1 = service.getCharacteristics()[UUID_DICT["lower vib"]] decval = int.from_bytes(subService1.read(), byteorder=sys.byteorder) 188,189c159,160 < subService = service.getCharacteristics()[UUID_DICT["light"]] < decval = int.from_bytes(subService.read(), byteorder=sys.byteorder)

subService1 = service.getCharacteristics()[UUID_DICT["light"]] decval = int.from_bytes(subService1.read(), byteorder=sys.byteorder)

——————————————————————————————

22,47d21 < getTilt: function (callback) { < request(this.baseUrl + "/getTilt", (err, resp, body) => { < if(err){ < this.log(err) < } < callback(null, parseInt(body)) < }); < }, < setTilt: function (value, callback) { < this.log('setting upper height ', value) < request(this.baseUrl + "/moveTilt/" + value, (err, resp, body) => { < callback(null, value) < }) < }, < getTiltStatus: function (callback) { < request(this.baseUrl + "/getTilt", (err, resp, body) => { < if(body == 0){ < this.isOn = false < } else { < this.log() < this.isOn = true < } < callback(null, this.isOn) < }) < }, < 210d183 < var tiltService = new Service.Lightbulb("Tilt Bed", "Tilt Bed"); 217,222d189 < tiltService.getCharacteristic(Characteristic.On) < .on('get', this.getTiltStatus.bind(this)) < tiltService.getCharacteristic(Characteristic.Brightness) < .on('get', this.getTilt.bind(this)) < .on('set', this.setTilt.bind(this)); < 260d226 < this.services.push(tiltService);

-- Scott Garrett @.*** http://www.technomancer.com

Some people are like slinkies - not really good for much of anything, but still bring a smile to your face when you push them down a flight *** of stairs.

On 28Nov, 2021, at 16:15, Jacob Byerline @.***> wrote:

Excellent, I am glad you got this figured out. If I remember correctly, when the "btle" instance which is named "dev" connects to the bed, the bed returns a list of available services to call. This is named "service". Then when we want to perform a particular action, we pick the sub-service we want by looking inside "service" for the right UUID. But these are referenced by index because "service" is a dictionary. So we pass in the index of the "subService" we want to perform. Once we have got that action queued up, we can then write some hexidecimal to it.

So you can probably print out "service" or "service.getCharacteristics()" and it should show you all the available services by uuid and their corresponding index.

As far as implementing this into the repo, I would like the code to be separated based on the model of bed. I have the Purple Powerbase made by Reverie, so we could add a constant called bed model at the top and basically say if(purple) use dictionary A. Else if (Reverie Model ABCD) use dictionary B

I like the idea of making it a service. When I have some time I will add that to the repo unless you beat me to it with a PR.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android.

jbyerline commented 2 years ago

I sort of took your code and ran with it… I’ve added more functions, expanded command set to the bed, put in a scanner to automatically find the bed, added some error checking, and documentation. I’m sure it’ll still be a work in progress, but I thought you might benefit from what I’ve done. I did, however rename the functions so it won’t drop in with your HomeKit plugin. My next goal is to rework that using different methods than light dimmers (maybe window controllers?). The timeouts on the dimmer are so short, there’s no way to use the function I added to wait until the bed reaches its position before releasing. That just causes homekit to freak out. Also, I’ve noticed that if you drag the sliders, it floods the python app with a LOT of requests, and sometimes they overrun each other. I did some work to alleviate that, but it’s not perfect. I created a separate project on github; feel free to lift whatever I did an put it in your codebase if you find it useful: https://github.com/tknmncr/reverie-powerbase/blob/main/reverie.py

Very nice job figuring it out! I would like to work with you to better implement the homebridge plugin. It would be simpler to have one central project rather than multiple forms for people to track down. I wrote this project in about a day when I was bored. I think a good strategy would be to use one homebrisge plugin and you can run a different flask api depending on the model of bed. But they can all be linked to the GitHub for the plugin.

I’m a full time student and software engineer so my schedules very full until I finish finals next week but after that I should have some more time to help out.

tknmncr commented 2 years ago

Yeah, that would be good.

I just created the github repo for mine just so I have a place to store it, and to learn how to use git and github. I’m an old school admin; I write shell scripts and work the *nix command line, but I’m not a regular software developer. I’ve been using Subversion for years and it’s worked fine for my needs. All my developer friends keep telling me I need to learn git, so… :)

I’ll make sure I refer people to yours as the main source. I just went a little overboard because I was using it as an exercise to learn python. This is my first python program ever (seriously; I’ve never even done “Hello, world!” in it before as I could do everything I’ve needed with bash). But the btle library in python was a compelling reason to learn it for this project. What took the most time was trying to find a way to make Flask die or let me move an exception out of Flask when there was a problem. I would prefer it to have a try: except: that would simply re-connect to the bed when it loses connection, but I still haven’t foudn a way that actually works properly. If I ever nail that, I’ll add it.

It’s the situation with javascript. I’ve done PHP and HTML, but never javascript, so I’ll be learning that as I go, too. I have to read up on Homebridge now, and see how it works at a deeper level.

Renaming the function calls in yours was all it took to get my bed working, but I would like to find a better way than a light dimmer. I love a lot of things about Homekit, but some of the decisions and omissions they have just leave me scratching my head.

Work has been slow lately, and I’m just trying to stay on top of things and keep my brain from atrophying.

-- Scott Garrett @.*** http://www.technomancer.com

    *** Keep the company of those who seek the truth, and run from those who have found it.  -- Vaclav Havel

On Dec 11, 2021, at 3:52 PM, Jacob Byerline @.***> wrote:

I sort of took your code and ran with it… I’ve added more functions, expanded command set to the bed, put in a scanner to automatically find the bed, added some error checking, and documentation. I’m sure it’ll still be a work in progress, but I thought you might benefit from what I’ve done. I did, however rename the functions so it won’t drop in with your HomeKit plugin. My next goal is to rework that using different methods than light dimmers (maybe window controllers?). The timeouts on the dimmer are so short, there’s no way to use the function I added to wait until the bed reaches its position before releasing. That just causes homekit to freak out. Also, I’ve noticed that if you drag the sliders, it floods the python app with a LOT of requests, and sometimes they overrun each other. I did some work to alleviate that, but it’s not perfect. I created a separate project on github; feel free to lift whatever I did an put it in your codebase if you find it useful: https://github.com/tknmncr/reverie-powerbase/blob/main/reverie.py https://github.com/tknmncr/reverie-powerbase/blob/main/reverie.py … <x-msg://3/#> Very nice job figuring it out! I would like to work with you to better implement the homebridge plugin. It would be simpler to have one central project rather than multiple forms for people to track down. I wrote this project in about a day when I was bored. I think a good strategy would be to use one homebrisge plugin and you can run a different flask api depending on the model of bed. But they can all be linked to the GitHub for the plugin.

I’m a full time student and software engineer so my schedules very full until I finish finals next week but after that I should have some more time to help out.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/jbyerline/flask-purple-powerbase/issues/1#issuecomment-991780445, or unsubscribe https://github.com/notifications/unsubscribe-auth/AC3FBLGHOUMGCW4TXVD4TMTUQO2XXANCNFSM5IVAGOWQ. Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.