Skip to content

Commit 0aa1315

Browse files
committed
Format as a package ready for upload to PyPi
1 parent 28784cf commit 0aa1315

File tree

7 files changed

+110
-35
lines changed

7 files changed

+110
-35
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
ref/
22
venv/
3-
config.yaml
43
.vscode
54
desk.pickle
65
__pycache__/
6+
*.egg-info
7+
dist/
8+
build/

README.md

+37-15
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ The desk should be connected and paired to the computer.
1010

1111
### Install
1212

13-
Install the Python requirements found in `requirements.txt`:
13+
Install using pip:
1414

1515
```
16-
pip install -r requirements.txt
16+
pip install idasen-controller
1717
```
1818

1919
### Configuration
2020

21-
Configuration can either be provided with a file, or via command line arguments. Use `--help` to see the command line arguments help. Edit `config.yaml` if you prefer your config to be in a file.
21+
Configuration can either be provided with a file, or via command line arguments. Use `--help` to see the command line arguments help. Edit `<config_dir>/config.yaml` if you prefer your config to be in a file. `<config_dir>` is normally `~/.config/idasen-controller` on Linux and `C:\Users\<user>\AppData\Local\idasen-controller\idasen-controller` on Windows.
2222

2323
Config options:
2424

@@ -30,10 +30,8 @@ Config options:
3030
- `scan_timeout` - Timeout to scan for the device (seconds). Default `5`
3131
- `connection_timeout` - Timeout to obtain connection (seconds). Default `10`
3232
- `movement_timeout` - Timeout for waiting for the desk to reach the specified height (seconds). Default `30`
33-
- `forward` - Forward commands to a script running as a server.
34-
- `server` - Run this script as a server to accept forwarded commands.
35-
- `server_address` - The address the server should run at. Default `127.0.0.1`
36-
- `server_port` - The port the server should run on. Default `9123`
33+
- `server_address` - The address the server should run at (if running server). Default `127.0.0.1`
34+
- `server_port` - The port the server should run on (if running server). Default `9123`
3735

3836
Device MAC addresses can be found using `bluetoothctl` and blueooth adapter names can be found with `hcitool dev` on linux, and on Windows you can use [Bluetooth LE Explorer](https://www.microsoft.com/en-us/p/bluetooth-le-explorer/9n0ztkf1qd98?activetab=pivot:overviewtab).
3937

@@ -44,55 +42,79 @@ Device MAC addresses can be found using `bluetoothctl` and blueooth adapter name
4442
To print the current desk height:
4543

4644
```
47-
python3 main.py
45+
idasen-controller
4846
```
4947

5048
To monitor for changes to height (and speed):
5149

5250
```
53-
python3 main.py --monitor
51+
idasen-controller --monitor
5452
```
5553

5654
Assuming the config file is populated to move the desk to standing position:
5755

5856
```
59-
python3 main.py --stand
57+
idasen-controller --stand
6058
```
6159

6260
Assuming the config file is populated to move the desk to sitting position:
6361

6462
```
65-
python3 main.py --sit
63+
idasen-controller --sit
6664
```
6765

6866
Move the desk to a certain height (mm) above the floor:
6967

7068
```
71-
python3 main.py --move-to 800
69+
idasen-controller --move-to 800
7270
```
7371

7472
Listing available bluetooth devices (using the configured `adapter_name`):
7573

7674
```
77-
python main.py --scan
75+
idasen-controller --scan
7876
```
7977

8078
To run the script as a server, which will maintain the connection and provide quicker response times:
8179

8280
```
83-
python main.py --server
81+
idasen-controller --server
8482
```
8583

8684
And to send commands to the server add the forward argument:
8785

8886
```
89-
python main.py --forward --stand
87+
idasen-controller --forward --stand
88+
```
89+
90+
To specify a path to a config file:
91+
92+
```
93+
idasen-controller --config <path>
9094
```
9195

96+
### Connection times
97+
98+
On Linux the script is able to cache the connection details so after the first connection subsequent commands should be very quick. On Windows this is not possible and so the script must scan for and connect to the desk every time a command is sent. One option is to reduce the `scan_timeout`. I have found that it can work well set to just `1` second. Also the server mode is intended as another workaround for this. Run the script once with `--server` which will start a persistent server and maintain a connection to the desk. Then when sending commands (like `--stand` or `--sit`) just add the additional argument `--forward` to forward the command to the server. The server should already have a connection so the desk should respond much quicker.
99+
92100
## Recipes
93101

94102
There is a page with a few examples of different ways to use the script: [RECIPES](RECIPES.md)
95103

104+
## Development
105+
106+
To run the script without installing via pip first install the requirements:
107+
108+
```
109+
pip install -r requirements.txt
110+
```
111+
112+
Then you can run all the same commands with:
113+
114+
```
115+
python idasen_controller/main.py <command>
116+
```
117+
96118
## Desk Internals
97119

98120
### Connection and Commands

idasen_controller/__init__.py

Whitespace-only changes.
File renamed without changes.

main.py idasen_controller/main.py

+32-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!python3
22
import os
3+
import shutil
34
import struct
45
import argparse
56
import yaml
@@ -8,6 +9,7 @@
89
import pickle
910
import json
1011
import functools
12+
from appdirs import user_config_dir
1113

1214
IS_LINUX = os.name == 'posix'
1315
IS_WINDOWS = os.name == 'nt'
@@ -38,8 +40,9 @@ def rawToSpeed(raw):
3840
COMMAND_REFERENCE_INPUT_DOWN = bytearray(struct.pack("<H", 32767))
3941

4042
# OTHER DEFINITIONS
41-
42-
PICKLE_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'desk.pickle')
43+
DEFAULT_CONFIG_DIR = user_config_dir('idasen-controller')
44+
DEFAULT_CONFIG_PATH = os.path.join(DEFAULT_CONFIG_DIR, 'config.yaml')
45+
PICKLE_FILE = os.path.join(DEFAULT_CONFIG_DIR, 'desk.pickle')
4346

4447
# CONFIGURATION SETUP
4548

@@ -49,6 +52,10 @@ def rawToSpeed(raw):
4952
MAX_HEIGHT = 1270 # 6500
5053

5154
# Default config
55+
if not os.path.isfile(DEFAULT_CONFIG_PATH):
56+
os.makedirs(os.path.dirname(DEFAULT_CONFIG_PATH), exist_ok=True)
57+
shutil.copyfile(os.path.join(os.path.dirname(__file__), 'example', 'config.yaml'), DEFAULT_CONFIG_PATH)
58+
5259
config = {
5360
"mac_address": None,
5461
"stand_height": BASE_HEIGHT + 420,
@@ -66,20 +73,6 @@ def rawToSpeed(raw):
6673
"server_port": 9123
6774
}
6875

69-
# Overwrite from config.yaml
70-
config_file = {}
71-
config_file_path = os.path.join(os.path.dirname(
72-
os.path.realpath(__file__)), 'config.yaml')
73-
if (config_file_path):
74-
with open(config_file_path, 'r') as stream:
75-
try:
76-
config_file = yaml.safe_load(stream)
77-
except yaml.YAMLError as exc:
78-
print("Reading config.yaml failed")
79-
exit(1)
80-
config.update(config_file)
81-
82-
# Overwrite from command line args
8376
parser = argparse.ArgumentParser(description='')
8477
parser.add_argument('--mac-address', dest='mac_address',
8578
type=str, help="Mac address of the Idasen desk")
@@ -103,6 +96,8 @@ def rawToSpeed(raw):
10396
help="The address the server should run at")
10497
parser.add_argument('--server_port', dest='server_port', type=int,
10598
help="The port the server should run on")
99+
parser.add_argument('--config', dest='config', type=str,
100+
help="File path to the config file (Default: {})".format(DEFAULT_CONFIG_PATH), default=DEFAULT_CONFIG_PATH)
106101
cmd = parser.add_mutually_exclusive_group()
107102
cmd.add_argument('--sit', dest='sit', action='store_true',
108103
help="Move the desk to sitting height")
@@ -117,8 +112,23 @@ def rawToSpeed(raw):
117112
cmd.add_argument('--server', dest='server', action='store_true',
118113
help="Run as a server to accept forwarded commands")
119114

120-
121115
args = {k: v for k, v in vars(parser.parse_args()).items() if v is not None}
116+
117+
# Overwrite config from config.yaml
118+
config_file = {}
119+
config_file_path = os.path.join(args['config'])
120+
if (config_file_path and os.path.isfile(config_file_path)):
121+
with open(config_file_path, 'r') as stream:
122+
try:
123+
config_file = yaml.safe_load(stream)
124+
except yaml.YAMLError as exc:
125+
print("Reading config.yaml failed")
126+
exit(1)
127+
else:
128+
print('No config file found')
129+
config.update(config_file)
130+
131+
# Overwrite config from command line args
122132
config.update(args)
123133

124134
if not config['mac_address']:
@@ -388,8 +398,11 @@ async def main():
388398
await disconnect(client)
389399
print('Disconnected ')
390400

391-
if __name__ == "__main__":
401+
def init():
392402
try:
393403
asyncio.run(main())
394404
except KeyboardInterrupt:
395-
pass
405+
pass
406+
407+
if __name__ == "__main__":
408+
init()

requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
appdirs==1.4.4
12
attrs==20.3.0
23
Automat==20.2.0
34
bleak==0.10.0

setup.py

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from os import path
2+
from setuptools import setup, find_packages
3+
4+
with open('requirements.txt') as f:
5+
requirements = f.readlines()
6+
7+
this_directory = path.abspath(path.dirname(__file__))
8+
with open(path.join(this_directory, 'README.md')) as f:
9+
long_description = f.read()
10+
11+
setup(
12+
name ='idasen-controller',
13+
version ='1.0.0',
14+
author ='Rhys Tyers',
15+
author_email ='',
16+
url ='https://github.com/rhyst/idasen-controller',
17+
description ='Command line tool for controlling the Ikea Idasen (Linak) standing desk',
18+
long_description = long_description,
19+
long_description_content_type ="text/markdown",
20+
license ='MIT',
21+
packages = find_packages(),
22+
entry_points ={
23+
'console_scripts': [
24+
'idasen-controller=idasen_controller.main:init'
25+
]
26+
},
27+
classifiers =(
28+
"Programming Language :: Python :: 3",
29+
"License :: OSI Approved :: MIT License",
30+
"Operating System :: OS Independent",
31+
),
32+
keywords ='python package idasen-controller idasen linak standing desk',
33+
install_requires = requirements,
34+
zip_safe = False,
35+
include_package_data = True,
36+
package_data={'': ['example/*']},
37+
)

0 commit comments

Comments
 (0)