Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
TurnTouch Python Library
Manage
Activity
Members
Labels
Plan
Issues
0
Issue boards
Milestones
Wiki
Code
Merge requests
0
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container Registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Anton Sarukhanov
TurnTouch Python Library
Commits
b9fc933b
Commit
b9fc933b
authored
6 years ago
by
Anton Sarukhanov
Browse files
Options
Downloads
Patches
Plain Diff
Handle button presses
parent
adc9e2a7
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
README.md
+100
-7
100 additions, 7 deletions
README.md
turntouch/__init__.py
+180
-11
180 additions, 11 deletions
turntouch/__init__.py
with
280 additions
and
18 deletions
README.md
+
100
−
7
View file @
b9fc933b
...
...
@@ -5,14 +5,13 @@ bluetooth smart home remote.
It is written in Python 3, originally for use with
[
Home Assistant
](
https://www.home-assistant.io/
)
.
# Status
This is currently pre-alpha status. It is not usable.
# Usage
## Scanning for Turn Touch devices
**Note:**
Scanning requires root privileges on Linux. To avoid this, skip
to the next section and connect to the device without scanning.
```
python
import
turntouch
...
...
@@ -26,9 +25,103 @@ device = turntouch.scan(only_one=True)[0]
devices
=
turntouch
.
scan
(
timeout
=
60
)
```
`turntouch.scan()`
returns a list of
`turntouch.TurnTouch`
objects.
## Interacting with a Turn Touch device
`turntouch.scan()`
returns a list of
`turntouch.TurnTouch`
objects. A connection
is automatically opened to each device, so it is ready to use.
`turntouch.TurnTouch`
is a subclass of
[
`bluepy.btle.Peripheral`
](
http://ianharvey.github.io/bluepy-doc/peripheral.html
)
.
## Interacting with a Turn Touch device
```
python
import
turntouch
# Connect to a device by MAC address
tt
=
turntouch
.
TurnTouch
(
'
c0:ff:ee:c0:ff:ee
'
)
# Read the device nickname
print
(
tt
.
name
)
# Update the device nickname (max. 32 characters)
tt
.
name
=
'
Living Room Remote
'
```
## Listening for button presses
```
python
from
turntouch
import
TurnTouch
,
DefaultButtonPressHandler
class
MyHandler
(
DefaultButtonPressHandler
):
def
button_north
(
self
):
print
(
"
Up button pressed.
"
)
def
button_east_double_tap
(
self
):
print
(
"
Right button double-tapped.
"
)
def
button_south_hold
(
self
):
print
(
"
Down button held.
"
)
tt
=
TurnTouch
(
'
c0:ff:ee:c0:ff:ee
'
)
tt
.
set_handler
(
MyHandler
())
tt
.
listen_forever
()
# One-liner alternative (same as listen_forever)
TurnTouch
(
'
c0:ff:ee:c0:ff:ee
'
,
handler
=
MyHandler
(),
listen
=
True
)
```
## More advanced usage
Here's a more complex example, triggering some existing functions.
```
python
import
turntouch
# Define a handler
class
MyFancyHandler
(
turntouch
.
DefaultButtonPressHandler
):
def
__init__
(
some_object
,
other_function
):
"""
Use the __init__ method to pass references to parts of your code,
such as objects, methods, or variables.
"""
self
.
thing_1
=
some_object
self
.
other_func
=
other_function
def
button_any
(
press_type
):
"""
Special handler which is fired for ALL button presses.
`press_type` is an instance of turntouch.PressType.
"""
if
press_type
.
name
==
"
North
"
:
self
.
thing_1
.
some_method
()
elif
press_type
.
name
in
[
"
South
"
,
"
East
"
,
"
West
"
]:
self
.
thing_1
.
other_method
()
else
:
self
.
other_func
()
def
button_south_hold
():
print
(
"
You can combine per-button handlers with button_any!
"
)
# Instantiate the handler, passing some application data into it
my_handler
=
MyFancyHandler
(
some_object_from_my_application
,
a_function
)
# Scan until we find a device
devices
=
[]
while
not
devices
:
devices
=
turntouch
.
scan
(
only_one
=
True
)
tt
=
devices
[
0
]
# Assign the handler to your device.
tt
.
set_handler
(
my_handler
)
tt
.
listen_forever
()
```
## Listening for just one button press
If you don't want the listener to run forever, do this:
```
python
tt
=
TurnTouch
(
'
c0:ff:ee:c0:ff:ee
'
,
handler
=
SomeHandler
)
tt
.
listen
()
# Will return as soon as one button is pressed.
```
## Error handling
Connection failures will raise
`turntouch.TurnTouchException`
. You may want to
catch and ignore this exception to retry connecting.
This diff is collapsed.
Click to expand it.
turntouch/__init__.py
+
180
−
11
View file @
b9fc933b
...
...
@@ -7,6 +7,74 @@ from typing import List, Union
logger
=
logging
.
getLogger
(
'
TurnTouch
'
)
class
PressType
:
"""
A type of button press.
"""
def
__init__
(
self
,
data
:
bytes
,
name
:
str
,
function_name
:
str
,
multi
:
bool
=
False
):
self
.
data
=
data
self
.
name
=
name
self
.
function_name
=
function_name
self
.
multi
=
multi
def
__repr__
(
self
):
return
'
<PressType name=
"
{name}
"
>
'
.
format
(
name
=
self
.
name
)
class
DefaultButtonPressHandler
:
"""
A callback handler class for button press events.
Create a subclass of this class to define your button press behavior.
"""
def
button_any
(
self
,
press_type
:
PressType
=
None
):
pass
def
button_off
(
self
):
pass
def
button_north
(
self
):
pass
def
button_north_double_tap
(
self
):
pass
def
button_north_hold
(
self
):
pass
def
button_east
(
self
):
pass
def
button_east_double_tap
(
self
):
pass
def
button_east_hold
(
self
):
pass
def
button_west
(
self
):
pass
def
button_west_double_tap
(
self
):
pass
def
button_west_hold
(
self
):
pass
def
button_south
(
self
):
pass
def
button_south_double_tap
(
self
):
pass
def
button_south_hold
(
self
):
pass
def
button_multi_north_east
(
self
):
pass
def
button_multi_north_west
(
self
):
pass
def
button_multi_north_south
(
self
):
pass
def
button_multi_east_west
(
self
):
pass
def
button_multi_east_south
(
self
):
pass
def
button_multi_west_south
(
self
):
pass
def
button_multi_north_east_west
(
self
):
pass
def
button_multi_north_east_south
(
self
):
pass
def
button_multi_north_west_south
(
self
):
pass
def
button_multi_east_west_south
(
self
):
pass
def
button_multi_north_east_west_south
(
self
):
pass
class
TurnTouch
(
btle
.
Peripheral
):
"""
A Turn Touch smart home remote.
"""
...
...
@@ -14,27 +82,78 @@ class TurnTouch(btle.Peripheral):
BATTERY_LEVEL_CHARACTERISTIC_UUID
=
'
2a19
'
DEVICE_NAME_CHARACTERISTIC_UUID
=
'
99c31526-dc4f-41b1-bb04-4e4deb81fadd
'
DEVICE_NAME_LENGTH
=
32
PRESS_TYPES
=
{
0xFF00
:
PressType
(
0xFF00
,
'
Off
'
,
'
button_off
'
),
0xFE00
:
PressType
(
0xFE00
,
'
North
'
,
'
button_north
'
),
0xEF00
:
PressType
(
0xEF00
,
'
North double tap
'
,
'
button_north_double_tap
'
),
0xFEFF
:
PressType
(
0xFEFF
,
'
North hold
'
,
'
button_north_hold
'
),
0xFD00
:
PressType
(
0xFD00
,
'
East
'
,
'
button_east
'
),
0xDF00
:
PressType
(
0xDF00
,
'
East double tap
'
,
'
button_east_double_tap
'
),
0xFDFF
:
PressType
(
0xFDFF
,
'
East hold
'
,
'
button_east_hold
'
),
0xFB00
:
PressType
(
0xFB00
,
'
West
'
,
'
button_west
'
),
0xBF00
:
PressType
(
0xBF00
,
'
West double tap
'
,
'
button_west_double_tap
'
),
0xFBFF
:
PressType
(
0xFBFF
,
'
West hold
'
,
'
button_west_hold
'
),
0xF700
:
PressType
(
0xF700
,
'
South
'
,
'
button_south
'
),
0x7F00
:
PressType
(
0x7F00
,
'
South double tap
'
,
'
button_south_double_tap
'
),
0xF7FF
:
PressType
(
0xF7FF
,
'
South hold
'
,
'
button_south_hold
'
),
0xFC00
:
PressType
(
0xFC00
,
'
Multi North East
'
,
'
button_multi_north_east
'
,
True
),
0xFA00
:
PressType
(
0xFA00
,
'
Multi North West
'
,
'
button_multi_north_west
'
,
True
),
0xF600
:
PressType
(
0xF600
,
'
Multi North South
'
,
'
button_multi_north_south
'
,
True
),
0xF900
:
PressType
(
0xF900
,
'
Multi East West
'
,
'
button_multi_east_west
'
,
True
),
0xF500
:
PressType
(
0xF500
,
'
Multi East South
'
,
'
button_multi_east_south
'
,
True
),
0xF300
:
PressType
(
0xF300
,
'
Multi West South
'
,
'
button_multi_west_south
'
,
True
),
0xF800
:
PressType
(
0xF800
,
'
Multi North East West
'
,
'
button_multi_north_east_west
'
,
True
),
0xF400
:
PressType
(
0xF400
,
'
Multi North East South
'
,
'
button_multi_north_east_south
'
,
True
),
0xF200
:
PressType
(
0xF200
,
'
Multi North West South
'
,
'
button_multi_north_west_south
'
,
True
),
0xF100
:
PressType
(
0xF100
,
'
Multi East West South
'
,
'
button_multi_east_west_south
'
,
True
),
0xF000
:
PressType
(
0xF000
,
'
Multi North East West South
'
,
'
button_multi_north_east_west_south
'
,
True
),
}
def
__init__
(
self
,
device_address
:
Union
[
str
,
btle
.
ScanEntry
],
interface
:
int
=
None
,
enable_notifications
:
bool
=
True
):
address
:
Union
[
str
,
btle
.
ScanEntry
],
handler
:
DefaultButtonPressHandler
=
None
,
listen
:
bool
=
False
,
interface
:
int
=
None
):
"""
Connect to the Turn Touch remote.
Set appropriate address type (overriding btle default).
:param
device_
address Union[str, btle.ScanEntry]:
:param address Union[str, btle.ScanEntry]:
MAC address (or btle.ScanEntry object) of this device
:param
interface int: Index of the bluetooth device (eg. 0 for hci0)
:param
enable_notifications bool: Start listening for button presses
"""
:param
listen bool: Start listening for button presses
:param
interface int: Index of the bluetooth device (eg. 0 for hci0)
"""
try
:
logger
.
info
(
"
Connecting to device {address}...
'
"
.
format
(
address
=
self
.
addr
))
logger
.
info
(
"
Connecting to device {address}...
"
.
format
(
address
=
addr
ess
))
super
(
TurnTouch
,
self
).
__init__
(
device_
address
,
btle
.
ADDR_TYPE_RANDOM
,
interface
)
logger
.
info
(
"
Successfully connected to device {address}.
'
"
.
format
(
address
,
btle
.
ADDR_TYPE_RANDOM
,
interface
)
logger
.
info
(
"
Successfully connected to device {address}.
"
.
format
(
address
=
self
.
addr
))
except
btle
.
BTLEException
:
raise
TurnTouchException
(
"
Failed to connect to device {address}
"
.
format
(
address
=
device_address
))
.
format
(
address
=
address
))
self
.
withDelegate
(
self
.
NotificationDelegate
(
turn_touch
=
self
))
self
.
set_handler
(
handler
)
if
listen
:
self
.
listen_forever
()
@property
def
name
(
self
)
->
str
:
...
...
@@ -58,6 +177,56 @@ class TurnTouch(btle.Peripheral):
logger
.
debug
(
"
Set name for device {address} to
'
{name}
'"
.
format
(
address
=
self
.
addr
,
name
=
name_bytes
))
def
set_handler
(
self
,
handler
:
DefaultButtonPressHandler
=
None
):
"""
Set the button press handler class for this remote.
"""
self
.
_handler
=
handler
or
DefaultButtonPressHandler
def
listen
(
self
,
timeout
:
int
=
0
,
only_one
:
bool
=
True
):
"""
Listen for a button press event.
Will listen indefinitely if `only_one` is False.
"""
self
.
_enable_notifications
()
if
only_one
:
self
.
waitForNotifications
(
timeout
)
else
:
while
True
:
self
.
waitForNotifications
(
timeout
)
self
.
_enable_notifications
(
enable
=
False
)
def
listen_forever
(
self
):
"""
Listen for button press events indefinitely.
"""
self
.
listen
(
only_one
=
False
)
def
_enable_notifications
(
self
,
enabled
=
True
):
"""
Tell the remote to start sending button press notifications.
"""
notification_handle
=
self
.
getCharacteristics
(
uuid
=
self
.
BUTTON_STATUS_CHARACTERISTIC_UUID
)[
0
].
getHandle
()
notification_enable_handle
=
notification_handle
+
1
logger
.
debug
(
"
{action} notifications for device {address}...
"
.
format
(
action
=
"
Enabling
"
if
enabled
else
"
Disabling
"
,
address
=
self
.
addr
))
self
.
writeCharacteristic
(
notification_enable_handle
,
bytes
([
0x01
if
enabled
else
0x00
,
0x00
]),
withResponse
=
True
)
logger
.
debug
(
"
Notifications {action} for device {address}.
"
.
format
(
action
=
"
enabled
"
if
enabled
else
"
disabled
"
,
address
=
self
.
addr
))
class
NotificationDelegate
(
btle
.
DefaultDelegate
):
"""
Handle callbacks for notifications from the device.
"""
def
__init__
(
self
,
turn_touch
):
"""
Retain a reference to the calling object.
"""
self
.
turn_touch
=
turn_touch
def
handleNotification
(
self
,
cHandle
,
data
):
"""
Call the appropriate button press handler method(s).
"""
logger
.
debug
(
"
Got notification {notification}
"
.
format
(
notification
=
data
))
type_int
=
int
.
from_bytes
(
data
,
byteorder
=
'
big
'
)
# Call the generic (any button) callback.
self
.
turn_touch
.
_handler
.
button_any
(
self
.
turn_touch
.
PRESS_TYPES
.
get
(
type_int
))
# Call the button-specific callback
getattr
(
self
.
turn_touch
.
_handler
,
self
.
turn_touch
.
PRESS_TYPES
.
get
(
type_int
).
function_name
)()
class
TurnTouchException
(
Exception
):
"""
An error related to the Turn Touch bluetooth smart home remote.
"""
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment