Skip to content

Commit 3452fc6

Browse files
committed
Add native support for multiple devices
1 parent c07eccb commit 3452fc6

File tree

10 files changed

+154
-66
lines changed

10 files changed

+154
-66
lines changed

.devcontainer/devcontainer.json

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"name": "Pixbyt",
3-
43
"initializeCommand": "git submodule update --init",
54
"postCreateCommand": {
65
".meltano": "ln -s /project/.meltano .",
7-
".env": "cp .env.sample .env"
6+
".env": "cp .env.sample .env",
7+
"apps.yml": "cp apps.yml.sample apps.yml",
8+
"devices.yml": "cp devices.yml.sample devices.yml"
89
},
9-
1010
"build": {
1111
"context": "..",
1212
"dockerfile": "../Dockerfile",
@@ -15,7 +15,6 @@
1515
},
1616
"cacheFrom": "type=gha"
1717
},
18-
1918
"customizations": {
2019
"codespaces": {
2120
"openFiles": [
@@ -32,13 +31,12 @@
3231
}
3332
}
3433
},
35-
3634
"secrets": {
3735
"TIDBYT_DEVICE_ID": {
3836
"description": "Optional. Find your Device ID in the Tidbyt mobile app under Settings > General > Get API Key."
3937
},
40-
"TIDBYT_TOKEN": {
41-
"description": "Optional. Find your API Token in the Tidbyt mobile app under Settings > General > Get API Key."
38+
"TIDBYT_KEY": {
39+
"description": "Optional. Find your Key in the Tidbyt mobile app under Settings > General > Get API Key."
4240
}
4341
}
4442
}

.env.sample

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,25 @@
22
# This is used by many apps that show the (relative) date and time.
33
TZ="America/Mexico_City"
44

5-
## target-tidbyt
6-
# Find your Device ID and API Token in the Tidbyt mobile app under Settings > General > Get API Key.
5+
### Devices
6+
# Find your Device ID and Key in the Tidbyt mobile app under Settings > General > Get API Key.
7+
# Multiple Tidbyts can be defined in devices.yml.
8+
9+
## Default
710
TIDBYT_DEVICE_ID="<device ID>"
8-
TIDBYT_TOKEN="<token>"
11+
TIDBYT_KEY="<key>"
12+
13+
# ## <name>
14+
# TIDBYT_<NAME>_DEVICE_ID="<device ID>"
15+
# TIDBYT_<NAME>_KEY="<key>"
16+
17+
### Apps
918

1019
## hello-world
1120
# This is used by the `hello-world` example app. Optionally, replace `world` with your own name.
1221
HELLO_WORLD_NAME="world"
1322

1423
# ## <app>
15-
# For any config key the app defines under `app_config:` in its `pixbyt.yml` file, add a value for the uppercase environment variable:
24+
# # For any config key the app defines under `app_config:` in its `pixbyt.yml` file, add a value for the uppercase environment variable:
1625
# <APP>_<CONFIG>="<value>"
1726

.github/workflows/main.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@ jobs:
4040
with:
4141
version: '2.20.2'
4242

43-
- name: Test / Create .env from sample
44-
run: cp .env.sample .env
43+
- name: Test / Create .env, apps.yml, and devices.yml from sample
44+
run: |
45+
cp .env.sample .env
46+
cp apps.yml.sample apps.yml
47+
cp devices.yml.sample devices.yml
4548
4649
- name: Test / Render hello-world to image
4750
run: |

README.md

Lines changed: 76 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ Apps running on Pixbyt have none of these limitations:
140140
Pixbyt lets you realize your wildest Tidbyt dreams by making it easy to:
141141
- **build** advanced Tidbyt apps,
142142
- **install** advanced apps built by the community,
143-
- **manage** app configurations and schedules,
143+
- **manage** multiple Tidbyts and apps,
144144
- **package** apps together in a [Docker](https://www.docker.com/) image, and
145145
- **launch** the app server using [Docker Compose](https://docs.docker.com/compose/).
146146

@@ -178,8 +178,7 @@ Codespaces will automatically install the necessary dependencies and launch you
178178

179179
1. Click the green "Use this template" button at the top of this page
180180
1. Choose "Open in a codespace" and wait for the codespace to start
181-
1. Update `.env` with your configuration:
182-
- `HELLO_WORLD_NAME`: Optionally, replace `world` with your own name.
181+
1. Optionally, update the `HELLO_WORLD_NAME` environment variable in `.env` to replace `world` with your own name.
183182
1. Render app to a WebP image file:
184183

185184
```bash
@@ -189,9 +188,8 @@ Codespaces will automatically install the necessary dependencies and launch you
189188
The image will be created at `output/hello-world/<timestamp>.webp`.
190189
The exact path is also printed in the command output.
191190
1. Render app to your Tidbyt:
192-
1. Update `.env` with your configuration:
193-
- `TIDBYT_DEVICE_ID`: Find your Device ID in the Tidbyt mobile app under Settings > General > Get API Key.
194-
- `TIDBYT_TOKEN`: Find your API Token in the Tidbyt mobile app under Settings > General > Get API Key.
191+
1. Find your Device ID and Key in the Tidbyt mobile app under Settings > General > Get API Key.
192+
1. Update the `TIDBYT_DEVICE_ID` and `TIDBYT_KEY` environment variables in `.env`.
195193
1. Render the `hello-world` app and send it to your Tidbyt (in the foreground):
196194

197195
```bash
@@ -224,8 +222,7 @@ If you haven't launched a codespace yet:
224222
225223
1. Click the green "Use this template" button at the top of this page
226224
1. Choose "Create a new repository"
227-
2. Create a new (private) repo
228-
225+
1. Create a new (private) repo
229226
1. Clone your new repository and enter the new directory:
230227
231228
```bash
@@ -235,27 +232,72 @@ If you haven't launched a codespace yet:
235232
236233
</details>
237234
238-
### 2. Configure your Tidbyt
239-
240-
1. If no `.env` configuration file exists yet, create one from the sample:
235+
### 2. Configure Pixbyt
241236
237+
1. If the `.env` configuration file doesn't exist yet, create it from the sample:
242238
```bash
243239
cp .env.sample .env
244240
```
241+
1. Update the `TZ` environment variable in `.env` to your ["TZ" timezone identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). This is used by many apps that show the (relative) date and time.
242+
243+
#### Option A: Configure just one Tidbyt
244+
245+
1. Find your Device ID and Key in the Tidbyt mobile app under Settings > General > Get API Key.
246+
1. Update the `TIDBYT_DEVICE_ID` and `TIDBYT_KEY` environment variables in `.env`.
247+
248+
<details>
249+
<summary>
250+
251+
#### Option B: Configure multiple Tidbyts
252+
253+
</summary>
254+
255+
1. If `devices.yml` doesn't exists yet, create it from the sample:
256+
```bash
257+
cp devices.yml.sample devices.yml
258+
```
259+
1. For each device:
260+
1. Add the device to `devices.yml` under `devices:`:
261+
```yaml
262+
devices:
263+
# ...
264+
- name: <name>
265+
id: $TIDBYT_<NAME>_DEVICE_ID
266+
key: $TIDBYT_<NAME>_KEY
267+
```
268+
269+
1. Replace `<name>` with the room name or another identifier.
270+
1. Replace `<NAME>` with the uppercased version thereof.
271+
272+
For example:
273+
274+
```yaml
275+
devices:
276+
# ...
277+
- name: office
278+
id: $TIDBYT_OFFICE_DEVICE_ID
279+
key: $TIDBYT_OFFICE_KEY
280+
```
281+
1. Find your Device ID and Key in the Tidbyt mobile app under Settings > General > Get API Key.
282+
1. Add the `TIDBYT_<NAME>_DEVICE_ID` and `TIDBYT_<NAME>_KEY` environment variables in `.env`.
283+
For example:
245284
246-
1. Update `.env` with your configuration:
247-
- `TZ`: Find your ["TZ" timezone identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). This is used by many apps that show the (relative) date and time.
248-
- `TIDBYT_DEVICE_ID`: Find your Device ID in the Tidbyt mobile app under Settings > General > Get API Key.
249-
- `TIDBYT_TOKEN`: Find your API Token in the Tidbyt mobile app under Settings > General > Get API Key.
285+
```bash
286+
TIDBYT_OFFICE_DEVICE_ID="foo-bar-baz-qux-abc"
287+
TIDBYT_OFFICE_KEY="<key>"
288+
```
289+
290+
</details>
250291
251292
### 3. Add your apps
252293
253-
The most important files in your Pixbyt repo (and likely the only ones you'll want to edit) are the following, which define the apps, their configuration, and their schedules:
294+
The most important files in your Pixbyt repo (and likely the only ones you'll want to edit) are the following, which define the apps, their configuration, and their apps:
254295

255296
```bash
256297
pixbyt
257-
├─ apps.yml # Schedules
258298
├─ .env # Configuration
299+
├─ apps.yml # App schedules
300+
├─ devices.yml # Optional: Devices
259301
└─ apps
260302
└─ <app> # One directory for each app
261303
├─ <app>.star # Main Pixlet applet
@@ -364,44 +406,49 @@ Skip ahead to step 4 to build and launch the app server.
364406
365407
#### 3.2. Configure the app
366408
367-
1. Add the app's update schedule to `apps.yml` under `schedules:`:
409+
1. Add the app and its update schedule to `apps.yml` under `apps:`:
368410
369411
```yaml
370-
schedules:
412+
apps:
371413
# ...
372414
- name: <app>
373-
interval: '<cron schedule expression>'
374-
job: <app>
415+
schedule: '<cron expression>'
416+
# If you have multiple Tidbyts defined in `devices.yml`, you can optionally filter them by name:
417+
# devices: [<device>]
375418
```
376419
377420
1. Replace `<app>` with the name of the app.
378-
1. Replace `<cron schedule expression>` with an appropriate [cron schedule expression](https://crontab.guru/):
421+
1. Replace `<cron expression>` with an appropriate [cron expression](https://crontab.guru/):
379422
380423
- Clocks should use `* * * * *` to update every minute, so that the displayed time is always as fresh as possible.
381424
- Apps that display a random entry from a list can use `*/5 * * * *` to update every 5 minutes, so that a fresh entry is shown on every app rotation.
382425
- Apps that show the latest data from some API can use `*/15 * * * *` to update every 15 minutes, or something else appropriate for your data source and the expected data freshness.
383426
- Apps that will always generate the same image can use `0 0 * * *` to update every day at midnight, just to be sure.
384427
385-
(A recommended schedule is typically documented in the app's `README`.)
428+
A recommended schedule is typically documented in the app's `README`.
429+
1. Optionally, replace `<device>` with the name of a device defined in `devices.yml` to only send the app to that device. By default, the app will be sent to all devices.
386430

387431
For example:
388432

389433
```yaml
390-
schedules:
434+
apps:
435+
# ...
391436
- name: hello-world
392-
interval: '0 * * * *' # On the hour
393-
job: hello-world
437+
schedule: '0/15 * * * *' # Every 15 minutes
438+
devices: [office] # Optional
394439
```
395440

396-
1. If the app requires configuration, update `.env`:
441+
Note that examples in apps' `README`s sometimes use `schedules` and `interval` keys instead of `apps` and `schedules`. They are equivalent and both are supported, but the latter is preferred.
442+
443+
1. If the app requires configuration, add its environment variables to `.env`:
397444
398445
For any config key the app defines under `app_config:` in its `pixbyt.yml` file, add a value for the uppercase environment variable:
399446
400447
```bash
401448
<APP>_<KEY>="<value>"
402449
```
403450
404-
(The exact keys are typically documented in the app's `README`.)
451+
The exact environment variables are typically documented in the app's `README`.
405452

406453
For example:
407454

@@ -484,7 +531,7 @@ Note that you'll need to do this each time your apps or their schedules change a
484531
```
485532
486533
Your Pixbyt app server is now running, and your apps will update on schedule!
487-
You can find logs for your apps under `logs/<app>/`.
534+
You can find logs for your apps under `logs/apps/<app>/`.
488535
489536
## How to develop apps with Pixbyt
490537

apps.yml

Lines changed: 0 additions & 8 deletions
This file was deleted.

apps.yml.sample

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apps:
2+
- name: hello-world
3+
schedule: '0 * * * *' # Every hour
4+
5+
# - name: <app>
6+
# schedule: '*/15 * * * *' # Every 15 minutes
7+
# devices: [<device>] # If you have multiple Tidbyts defined in devices.yml, you can optionally filter them by name.

apps/hello-world/client.star

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,9 @@ def client.get_image():
44
return file.read("logo.png")
55

66
def client.get_response(name):
7-
return file.exec("hello.py", {"name": name})["response"]
7+
input = {"name": name}
8+
output = file.exec("hello.py", input)
9+
10+
print("%s ---hello.py--> %s" % (input, output))
11+
12+
return output["response"]

devices.yml.sample

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
devices:
2+
- name: default
3+
id: $TIDBYT_DEVICE_ID
4+
key: $TIDBYT_KEY
5+
6+
# - name: <room name or other identifier>
7+
# id: $TIDBYT_<NAME>_DEVICE_ID
8+
# key: $TIDBYT_<NAME>_KEY

plugins/airflow/dags/pixbyt.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"start_date": datetime(1970, 1, 1, 0, 0, 0),
2525
}
2626
}
27-
DEFAULT_INTERVAL = "*/15 * * * *"
27+
DEFAULT_SCHEDULE = "*/15 * * * *"
2828

2929
PROJECT_ROOT = os.getenv("MELTANO_PROJECT_ROOT", os.getcwd())
3030
MELTANO_EXECUTABLE = ".meltano/run/bin"
@@ -33,24 +33,38 @@
3333
apps_path = Path(PROJECT_ROOT).joinpath(APPS_FILENAME)
3434
apps_config = yaml.safe_load(apps_path.read_text())
3535

36-
schedules = apps_config.get("schedules", [])
36+
if isinstance(apps_config, list):
37+
apps_config = {"apps": apps_config} # `apps:` is optional
38+
apps = [
39+
*apps_config.get("apps", []),
40+
*apps_config.get("schedules", []) # Backwards compatibility with meltano.yml format
41+
]
3742

38-
for schedule in schedules:
39-
name = schedule.get("name")
43+
for app in apps:
44+
name = app.get("name")
4045

4146
if not name:
4247
logger.warning("Skipping app without a name")
4348
continue
4449

45-
interval = schedule.get("interval", DEFAULT_INTERVAL)
46-
job = schedule.get("job", name)
50+
schedule = (
51+
app.get("schedule")
52+
or app.get("interval") # Backwards compatibility with meltano.yml format
53+
or DEFAULT_SCHEDULE
54+
)
55+
job = app.get("job", name)
4756

48-
env = schedule.get("env", {})
49-
env = {k: str(v) for k, v in env.items()}
57+
env = app.get("env", {}) # Backwards compatibility with meltano.yml format
58+
59+
devices = app.get("devices", [])
60+
if devices:
61+
env["TIDBYT_DEVICE_NAMES"] = str(devices)
5062

5163
dag_id = name.replace("/", "--")
52-
with DAG(dag_id, schedule=interval, **DEFAULT_DAG_OPTS) as dag:
64+
65+
with DAG(dag_id, schedule=schedule, **DEFAULT_DAG_OPTS) as dag:
5366
cmd = f"{MELTANO_EXECUTABLE} run {job}"
67+
env = {k: str(v) for k, v in env.items()}
5468

5569
task = BashOperator(
5670
dag=dag,
@@ -62,4 +76,4 @@
6276
)
6377
globals()[dag_id] = dag
6478

65-
logger.info(f"Created DAG '{dag_id}': interval='{interval}', cmd='{cmd}', env={env}")
79+
logger.info(f"Created DAG '{dag_id}': schedule='{schedule}', cmd='{cmd}', env={env}")

plugins/plugins.meltano.yml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@ plugins:
1919
namespace: target_tidbyt
2020
pip_url: git+https://github.com/DouweM/target-tidbyt.git
2121
settings:
22-
- name: token
23-
kind: password
2422
- name: device_id
25-
config:
26-
device_id: $TIDBYT_DEVICE_ID
27-
token: $TIDBYT_TOKEN
23+
value: $TIDBYT_DEVICE_ID
24+
- name: key
25+
kind: password
26+
value: $TIDBYT_KEY
27+
28+
- name: devices_path
29+
value: $MELTANO_PROJECT_ROOT/devices.yml
30+
- name: device_names
31+
kind: array
32+
value: $TIDBYT_DEVICE_NAMES
2833

2934
- name: target-webp
3035
namespace: target_webp

0 commit comments

Comments
 (0)