Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite in nim #5

Merged
merged 165 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
165 commits
Select commit Hold shift + click to select a range
0f68e37
nim test
mariusandra Dec 14, 2023
9520b2c
nim test
mariusandra Dec 14, 2023
9f6b054
trying things
mariusandra Dec 16, 2023
512b3b2
trying things
mariusandra Dec 16, 2023
ab16928
assets
mariusandra Dec 17, 2023
e02a7b6
split more
mariusandra Dec 19, 2023
03f9fed
split
mariusandra Dec 19, 2023
2667980
assets and nimpretty
mariusandra Dec 19, 2023
d6f0a7f
get some kind of cross copilation working
mariusandra Dec 19, 2023
73f5872
random unsplash
mariusandra Dec 19, 2023
718b1d5
measure time
mariusandra Dec 19, 2023
9b60a86
basic config
mariusandra Dec 20, 2023
e8ef3ad
fix SSH log bug
mariusandra Dec 20, 2023
6e903e5
fix save bug
mariusandra Dec 20, 2023
6057027
fix new frame bug
mariusandra Dec 20, 2023
8644fc4
deploy nim source
mariusandra Dec 20, 2023
2c56c31
more verbose compilation
mariusandra Dec 21, 2023
1edb42c
actual log messages
mariusandra Dec 21, 2023
dd5d529
nim tests
mariusandra Dec 22, 2023
b1f0e8b
move config
mariusandra Dec 22, 2023
b52177c
change default ports to 8787 (frame) and 8989 (controller)
mariusandra Dec 22, 2023
10c0b1d
split in two
mariusandra Dec 22, 2023
96a0e5f
logger cleanup
mariusandra Dec 22, 2023
c8c5ebe
misc
mariusandra Dec 22, 2023
4ba89e3
move things around. beginnings of a scene
mariusandra Dec 23, 2023
fd70929
first steps towards custom builds
mariusandra Dec 23, 2023
0614a94
app cleanup
mariusandra Dec 23, 2023
1944051
make types file
mariusandra Dec 23, 2023
cc6d583
renderer
mariusandra Dec 23, 2023
f1c1000
send config message on boot
mariusandra Dec 23, 2023
5d66146
get all the output
mariusandra Dec 23, 2023
608752b
better error on bad source
mariusandra Dec 23, 2023
ad14b9b
app
mariusandra Dec 23, 2023
b98401d
actually run some apps
mariusandra Dec 24, 2023
2c5e375
fix
mariusandra Dec 24, 2023
3c8d9bd
config -> frameConfig
mariusandra Dec 24, 2023
6fa6cd5
app config
mariusandra Dec 24, 2023
d55e0a7
debug, unsplash fixes, timeouts
mariusandra Dec 24, 2023
daf9fa5
misc
mariusandra Dec 24, 2023
fe0870f
unsplash app seconds
mariusandra Dec 24, 2023
b87023c
font utils
mariusandra Dec 24, 2023
7608566
font utils
mariusandra Dec 24, 2023
57cef7b
longer default
mariusandra Dec 24, 2023
9563003
button cleanup
mariusandra Dec 24, 2023
a615b7c
one less
mariusandra Dec 25, 2023
1c72ea7
log time
mariusandra Dec 25, 2023
1cea976
misc cleanup
mariusandra Dec 25, 2023
91285ca
pass frameOS to apps directly
mariusandra Dec 25, 2023
a386c8e
fonts and refactor
mariusandra Dec 25, 2023
f368ad1
text overlay works (if not too efficient)
mariusandra Dec 25, 2023
882108f
show scene source
mariusandra Dec 25, 2023
5db7442
app config types integer, float, color
mariusandra Dec 25, 2023
31110f7
misc cleanup
mariusandra Dec 25, 2023
ea93f6b
cache font rendering
mariusandra Dec 25, 2023
3562bf7
render on boot
mariusandra Dec 25, 2023
de0dd94
debug timing option
mariusandra Dec 25, 2023
3a59fb7
match events
mariusandra Dec 25, 2023
1b82594
rename
mariusandra Dec 25, 2023
cd4b0f1
return cached image
mariusandra Dec 25, 2023
dd2ea28
code nodes
mariusandra Dec 26, 2023
93b86a2
code nodes
mariusandra Dec 26, 2023
5cc16db
code nodes
mariusandra Dec 26, 2023
f058b1d
no newlines
mariusandra Dec 26, 2023
b544b65
rename frameos/ to frameos-python/
mariusandra Dec 26, 2023
690e4f0
rename frame to frameos
mariusandra Dec 26, 2023
0ae22c4
rename frame to frameos
mariusandra Dec 26, 2023
59b7d3f
fix img reload
mariusandra Dec 26, 2023
59f22d1
support custom apps
mariusandra Dec 26, 2023
70e192e
all kinds of updates
mariusandra Dec 26, 2023
bb1a217
state example
mariusandra Dec 27, 2023
5c14f52
rename to .run
mariusandra Dec 27, 2023
6fce693
some async
mariusandra Dec 27, 2023
811429d
render loop
mariusandra Dec 27, 2023
4c573a3
trigger rerender
mariusandra Dec 27, 2023
d7349ec
cleanup
mariusandra Dec 27, 2023
ae29eec
clock app
mariusandra Dec 27, 2023
4cfb2c0
more dates
mariusandra Dec 27, 2023
5585d2d
less hammering
mariusandra Dec 27, 2023
c1aa4e0
less noise
mariusandra Dec 27, 2023
e8a1210
make sure the folder exists
mariusandra Dec 27, 2023
1833fe2
inky driver
mariusandra Dec 27, 2023
5771982
make inky work
mariusandra Dec 27, 2023
db024e0
basic hdmi driver
mariusandra Dec 28, 2023
41063f6
log in thread
mariusandra Dec 28, 2023
a45d745
download image app
mariusandra Dec 28, 2023
f45fb74
longer timeout
mariusandra Dec 28, 2023
9ba57d1
correct path
mariusandra Dec 28, 2023
c073eb1
needs types
mariusandra Dec 28, 2023
7175e07
almost full rotation
mariusandra Dec 28, 2023
d64415b
system of drivers and architectures
mariusandra Dec 28, 2023
069a9f6
rotation
mariusandra Dec 28, 2023
944beff
cached on device compilation
mariusandra Dec 28, 2023
8f2936a
remove build zip
mariusandra Dec 28, 2023
f04a840
some methods
mariusandra Dec 29, 2023
b6eb70f
galleries app
mariusandra Dec 29, 2023
ae225c9
no rerenders
mariusandra Dec 29, 2023
8982335
skip rerendering
mariusandra Dec 29, 2023
89ab4ed
color app
mariusandra Dec 29, 2023
1c4da3b
gradient app
mariusandra Dec 29, 2023
1861f4f
ha sensor app
mariusandra Dec 29, 2023
4e9f043
cache
mariusandra Dec 29, 2023
80e6075
bool fields and if else
mariusandra Dec 29, 2023
57ce41d
debug bool
mariusandra Dec 29, 2023
1636db7
split app
mariusandra Dec 29, 2023
800cf47
cleanup handles
mariusandra Dec 29, 2023
36d7ed4
loop index and key
mariusandra Dec 29, 2023
3af9bc9
highlight
mariusandra Dec 29, 2023
f05cb0b
resize app
mariusandra Dec 29, 2023
02b56a3
add frameConfig
mariusandra Dec 29, 2023
672cf29
rotation app
mariusandra Dec 30, 2023
308f2f2
bool fix
mariusandra Dec 30, 2023
f23ff5c
download image timeout
mariusandra Dec 30, 2023
1d7bfe3
openAI app
mariusandra Dec 30, 2023
c084d4d
rename inky to inkyPython
mariusandra Dec 30, 2023
d7520cd
camelcased frame.json
mariusandra Dec 30, 2023
8775f3d
inline log
mariusandra Dec 30, 2023
bd8b49d
batched logging
mariusandra Dec 30, 2023
b2d8f8f
driver cleanup
mariusandra Dec 30, 2023
c4f51db
dalle 3
mariusandra Dec 30, 2023
5eb784b
fix
mariusandra Dec 30, 2023
37bb881
render async, get fbinfo
mariusandra Dec 30, 2023
92fa0c1
drivers can be initialized
mariusandra Dec 30, 2023
3501826
write to framebuffer
mariusandra Dec 30, 2023
6f0b079
slow down image reloads
mariusandra Dec 30, 2023
036802d
update width and height from framebuffer
mariusandra Dec 30, 2023
e0173fd
just in case
mariusandra Dec 30, 2023
f2e07d5
fix rotated inky rendering
mariusandra Dec 30, 2023
1ce726d
sanitize strings
mariusandra Dec 30, 2023
ca522ce
clean up ported files from frameos-python
mariusandra Dec 30, 2023
a887284
less log
mariusandra Dec 30, 2023
c5201d2
turn screen on or off
mariusandra Dec 31, 2023
69dcaff
pause logging
mariusandra Dec 31, 2023
5c70397
hyperpixel 2r
mariusandra Dec 31, 2023
d901b36
easier
mariusandra Dec 31, 2023
e299e14
if in 32bit
mariusandra Dec 31, 2023
13aeb09
empty metrics logger
mariusandra Dec 31, 2023
af522d5
HA nil
mariusandra Dec 31, 2023
e90cb83
faster BGR
mariusandra Dec 31, 2023
010b30d
metrics
mariusandra Dec 31, 2023
129fe39
metrics try
mariusandra Dec 31, 2023
2dacaae
faster rendering
mariusandra Dec 31, 2023
f44ad52
with a dot
mariusandra Dec 31, 2023
4d82778
rollback
mariusandra Dec 31, 2023
fee4b9d
actually working waveshare driver
mariusandra Jan 1, 2024
aa5d6a6
tweaks
mariusandra Jan 1, 2024
bac43aa
waveshare updates
mariusandra Jan 2, 2024
c7cb665
waveshare update
mariusandra Jan 2, 2024
59b3eef
select waveshare variant
mariusandra Jan 2, 2024
926f91d
cleanup
mariusandra Jan 2, 2024
8f0ec2d
fix
mariusandra Jan 2, 2024
f0a8bfe
nim errors in frontend
mariusandra Jan 2, 2024
d7744d2
evdev
mariusandra Jan 2, 2024
a47507b
mouse move
mariusandra Jan 2, 2024
0098508
ev:
mariusandra Jan 2, 2024
3b7eb82
capitalize
mariusandra Jan 2, 2024
e40d282
unified events channel
mariusandra Jan 2, 2024
5088d1c
log all events
mariusandra Jan 2, 2024
e68e4c6
mark events with rendering=bool
mariusandra Jan 2, 2024
27cc5fd
async events while rendering, mouse move coordinates
mariusandra Jan 2, 2024
528afeb
break if rendering
mariusandra Jan 2, 2024
2a6f00f
Merge branch 'main' into nim
mariusandra Jan 2, 2024
7519302
readme
mariusandra Jan 2, 2024
c22fed0
remove waveshare
mariusandra Jan 2, 2024
e007af7
readme
mariusandra Jan 2, 2024
1cf7381
update dockerfile
mariusandra Jan 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ dist-ssr
*.sln
*.sw?
*Type.ts

# Nim
frameos/tmp/
21 changes: 19 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Use the official Python 3.9 image as the base
FROM python:3.9-slim-bullseye
FROM python:3.11-slim-bullseye

# Set the working directory
WORKDIR /app
Expand All @@ -13,6 +13,23 @@ RUN apt-get update && apt-get install -y curl build-essential libffi-dev redis-s
&& apt-get update \
&& apt-get install -y nodejs

# Install Nim
RUN apt-get update && \
apt-get install -y curl xz-utils gcc openssl ca-certificates git # &&

RUN mkdir -p /opt/nim && \
curl -L https://nim-lang.org/download/nim-2.0.2.tar.xz | tar -xJf - -C /opt/nim --strip-components=1 && \
cd /opt/nim && \
sh build.sh && \
bin/nim c koch && \
./koch boot -d:release && \
./koch tools

ENV PATH="/opt/nim/bin:${PATH}"

RUN nim --version \
nimble --version

# Copy the requirements file and install using pip
WORKDIR /app/backend
COPY backend/requirements.txt .
Expand Down Expand Up @@ -51,7 +68,7 @@ COPY . .

RUN rm -rf /app/frontend && mv /tmp/frontend /app/

EXPOSE 8999
EXPOSE 8989

# Start huey in the background and then run the Flask application
CMD ["sh", "-c", "redis-server --daemonize yes && cd backend && flask db upgrade && huey_consumer.py app.huey.huey --worker-type=greenlet --workers=10 --flush-locks & cd backend && python3 run.py"]
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
# FrameOS - smart home frames
# FrameOS

FrameOS is a tool for controlling Raspberry Pi-powered e-ink displays, a repository of apps to run on them, and an IDE for writing your own.
FrameOS is an **operating system for single function smart frames**.

It's meant to be deployed on a Raspberry Pi, and can be used with a variety of e-ink and traditional displays. It's designed for both screens that update **60 seconds per frame**, and for screens that update **60 frames per second**.

Think smart home calendars, meeting room displays, thermostats, industrial dashboards, public advertisement screens, and more.

To get started:

1. Install the [FrameOS controller](/installation/controller), a dockerized python app, which is used to deploy apps onto individual frames via SSH.
1. Install the [FrameOS controller](https://frameos.net/installation/controller), a dockerized python app, which is used to deploy apps onto individual frames via SSH.

2. Read the [device hardware guide](/devices) for your screen type. Typically you'll just need to connect the display to a Raspberry Pi, install the OS, and make sure it's reachable over the network.
2. Read the [device hardware guide](https://frameos.net/devices/) for your screen type. Typically you'll just need to connect the display to a Raspberry Pi, install the OS, and make sure it's reachable over the network.

3. Once connected, deploy our prebuilt [apps](/apps), or code your own directly inside the controller.
3. Once connected, deploy our prebuilt [apps](https://frameos.net/apps/), or code your own directly inside the controller.

4. Finally, for a professional look, 3d print a case around your frame, or order one online.
4. Finally, for a professional look, 3d print a case around your frame.

![](https://frameos.net/assets/images/walkthrough-c32e7b67dd9a6f14ebef743755b0fc8e.gif)

Expand All @@ -25,7 +29,7 @@ We support all the most common e-ink displays out there.
- Framebuffer HDMI output
- Web server kiosk mode

[See the full list here!](/devices)
[See the full list here!](https://frameos.net/devices/)

![](https://frameos.net/assets/images/1-frames-d127cdd40eaec7b65932a78a7a2034ae.jpg)

Expand All @@ -40,7 +44,7 @@ Read more in [the documentation](https://frameos.net/installation/controller).
```bash
# running the latest release
SECRET_KEY=$(openssl rand -base64 32)
docker run -d -p 8999:8999 -v ./db:/app/db --name frameos --restart always -e SECRET_KEY="$SECRET_KEY" frameos/frameos
docker run -d -p 8989:8989 -v ./db:/app/db --name frameos --restart always -e SECRET_KEY="$SECRET_KEY" frameos/frameos

# update daily to the latest release
docker run -d \
Expand Down
6 changes: 4 additions & 2 deletions backend/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
migrate = Migrate()
socketio = SocketIO(async_mode='gevent')

DEFAULT_REDIS_URL = 'redis://localhost:6379/0'

# Redis setup
def create_redis_connection():
redis_url = os.environ.get('REDIS_URL', 'redis://localhost:6379/0')
redis_url = os.environ.get('REDIS_URL', DEFAULT_REDIS_URL)
parsed_url = urlparse(redis_url)
redis_host = parsed_url.hostname
redis_port = parsed_url.port or 6379
Expand Down Expand Up @@ -61,7 +63,7 @@ def create_app(config: Optional[Config] = None):
db.create_all()
login_manager.init_app(app)
migrate.init_app(app, db)
socketio.init_app(app, cors_allowed_origins="*", message_queue=os.environ.get('REDIS_URL'))
socketio.init_app(app, cors_allowed_origins="*", message_queue=os.environ.get('REDIS_URL', DEFAULT_REDIS_URL))
initialize_sentry(app)

from app.views.base import setup_base_routes
Expand Down
99 changes: 48 additions & 51 deletions backend/app/api/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import ast
import re
import shutil

import requests
import json
import tempfile
import os
import subprocess

from flask import jsonify, request
from flask_login import login_required
Expand Down Expand Up @@ -29,6 +35,8 @@ def validate_python_frame_source():

if file.endswith('.py'):
errors = validate_python(source)
elif file.endswith('.nim'):
errors = validate_nim(source)
elif file.endswith('.json'):
errors = validate_json(source)
else:
Expand All @@ -53,58 +61,12 @@ def enhance_python_frame_source():
return jsonify({"error": "OpenAI API key not set"}), 400

ai_context = f"""
You are helping a python developer write ea FrameOS application. You are editing frame.py, the main file in FrameOS.
This controls an e-ink display and runs on a Raspberry Pi. Help the user with their changes.

This is what we inherit from:
```python
class FrameConfig:
status: str
version: str
width: int
height: int
device: str
color: str
interval: float
scaling_mode: str
background_color: str
rotate: int
scenes: List[FrameConfigScene]
settings: Dict
class ExecutionContext:
event: str
payload: Dict
image: Optional[Image]
state: Dict
apps_ran: List[str]
apps_errored: List[str]
class App:
def __post_init__(self):
def rerender(self, trigger = None):
def is_rendering(self):
def break_execution(self, message: Optional[str] = None):
def log(self, message: str):
def error(self, message: str):
def get_config(self, key: str, default = None):
def get_setting(self, key: Union[str, List[str]], default = None):
def parse_str(self, text: str, state: Dict):
def dispatch(self, event: str, payload: Optional[Dict] = None, image: Optional[Image] = None) -> ExecutionContext:
def shell(self, command: str):
def apt(self, package: str):
def run(self, payload: ExecutionContext):
# code goes here, does not need to call super
```

From image_utils you can import:
scale_image(image: Image.Image, requested_width: int, requested_height: int, scaling_mode: 'cover' | 'contain' | 'center' | 'stretch', background_color: str) -> image.Image
draw_text_with_border(draw, position, text, font, font_color, border_color, border_width=1, align='left'):
You are helping a python developer write ea FrameOS application. You are editing app.nim, the main file in FrameOS.
This controls an e-ink display and runs on a Raspberry Pi. Help the user with their changes. Be mindful of what
you do and do not know.

Pip packages can be installed with code like self.shell("pip3 install selenium==4.14.0") in __post_init__().

Currently available: bidict==0.22.1 certifi==2023.7.22 charset-normalizer==3.2.0 click==8.1.6 dacite==1.8.1 evdev==1.6.1 flask==2.2.5 flask-socketio==5.3.4 idna==3.4 importlib-metadata==6.7.0 inky==1.5.0 itsdangerous==2.1.2 jinja2==3.1.2 markupsafe==2.1.3 numpy==1.26.1 pillow==9.5.0 psutil==5.9.6 python-engineio==4.5.1 python-socketio==5.8.0 requests==2.31.0 rpi-gpio==0.7.1 smbus2==0.4.2 spidev==3.6 urllib3==2.0.4 werkzeug==2.2.3 zipp==3.15.0

This is the current source of frame.py:
```python
This is the current source of app.nim:
```nim
{source}
```
"""
Expand Down Expand Up @@ -145,6 +107,41 @@ def validate_python(source):
return [{"line": e.lineno, "column": e.offset, "error": str(e)}]


def validate_nim(source):
try:
with tempfile.TemporaryDirectory() as temp_dir:
# copy src/frameos/types.nim to temp_dir
target_path = os.path.join(temp_dir, "frameos")
os.makedirs(target_path, exist_ok=True)
shutil.copytree("../frameos/src/frameos", target_path, dirs_exist_ok=True)

temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.nim', dir=temp_dir, delete=False)
temp_file_name = temp_file.name
temp_file_abs_name = os.path.realpath(temp_file_name)
temp_file.write(source)
temp_file.close()

result = subprocess.run(['nim', 'check', temp_file_name], capture_output=True, text=True)

errors = []
for line in result.stderr.split('\n'):
if line.startswith(temp_file_name) or line.startswith(temp_file_abs_name):
# "tmps4sk1v2t.nim(22, 12) Error: expression 'scene' has no type (or is ambiguous)"
if line.startswith(temp_file_name):
line = line[len(temp_file_name):]
elif line.startswith(temp_file_abs_name):
line = line[len(temp_file_abs_name):]

if "Error:" in line:
match = re.search(r'\((\d+), (\d+)\) (Error: .+)', line)
if match:
line_no, column, error = int(match.group(1)), int(match.group(2)), match.group(3)
errors.append({"line": line_no, "column": column, "error": error})
return errors

except Exception as e:
return [{"error": str(e)}]

def validate_json(source):
try:
json.loads(source)
Expand Down
23 changes: 18 additions & 5 deletions backend/app/api/frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from flask_login import login_required
from . import api
from app import redis
from app.models.frame import Frame, new_frame, delete_frame, update_frame
from app.models.frame import Frame, new_frame, delete_frame, update_frame, generate_scene_nim_source


@api.route("/frames", methods=["GET"])
@login_required
Expand Down Expand Up @@ -43,14 +44,15 @@ def api_frame_get_logs(id: int):
def api_frame_get_image(id: int):
frame = Frame.query.get_or_404(id)
cache_key = f'frame:{frame.frame_host}:{frame.frame_port}:image'
url = f'http://{frame.frame_host}:{frame.frame_port}/image'

try:
if request.args.get('t') == '-1':
last_image = redis.get(cache_key)
if last_image:
return Response(last_image, content_type='image/png')

response = requests.get(f'http://{frame.frame_host}:{frame.frame_port}/image')
response = requests.get(url, timeout=15)
if response.status_code == 200:
redis.set(cache_key, response.content, ex=86400 * 30) # cache for 30 days
return Response(response.content, content_type='image/png')
Expand All @@ -59,6 +61,8 @@ def api_frame_get_image(id: int):
if last_image:
return Response(last_image, content_type='image/png')
return jsonify({"error": "Unable to fetch image"}), response.status_code
except requests.exceptions.Timeout:
return jsonify({'error': f'Request Timeout to {url}'}), HTTPStatus.REQUEST_TIMEOUT
except Exception as e:
return jsonify({'error': 'Internal Server Error', 'message': str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR

Expand All @@ -75,6 +79,15 @@ def api_frame_render_event(id: int):
except Exception as e:
return jsonify({'error': 'Internal Server Error', 'message': str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR

@api.route('/frames/<int:id>/scene_source/<scene>', methods=['GET'])
@login_required
def api_frame_scene_source(id: int, scene: str):
frame = Frame.query.get_or_404(id)
scene = [scene for scene in frame.scenes if scene.get('id') == 'default'][0]
if not scene:
return jsonify({'error': f'Scene {scene} not found'}), HTTPStatus.NOT_FOUND
return jsonify({'source': generate_scene_nim_source(frame, scene)})

@api.route('/frames/<int:id>/reset', methods=['POST'])
@login_required
def api_frame_reset_event(id: int):
Expand Down Expand Up @@ -112,17 +125,17 @@ def api_frame_update(id: int):
fields = ['scenes', 'name', 'frame_host', 'frame_port', 'ssh_user', 'ssh_pass', 'ssh_port', 'server_host',
'server_port', 'server_api_key', 'width', 'height', 'rotate', 'color', 'interval', 'metrics_interval',
'scaling_mode', 'background_color', 'device']
defaults = {'frame_port': 8999, 'ssh_port': 22}
defaults = {'frame_port': 8787, 'ssh_port': 22}
try:
payload = request.json
for field in fields:
if field in payload:
value = payload[field]
if value == '' or value == 'null':
value = defaults.get(field, None)
elif field in ['frame_port', 'ssh_port', 'width', 'height', 'rotate']:
elif field in ['frame_port', 'ssh_port', 'width', 'height', 'rotate'] and value is not None:
value = int(value)
elif field in ['interval', 'metrics_interval']:
elif field in ['interval', 'metrics_interval'] and value is not None:
value = float(value)
elif field in ['scenes']:
if type(value) == str:
Expand Down
12 changes: 6 additions & 6 deletions backend/app/api/tests/test_frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def test_api_frame_update_a_lot(self):
'ssh_pass': 'herring',
'ssh_port': '2222',
'server_host': 'walrus',
'server_port': '89999',
'server_port': '89898',
'device': 'framebuffer',
'scaling_mode': 'contain',
'rotate': '90',
Expand All @@ -143,7 +143,7 @@ def test_api_frame_update_a_lot(self):
assert updated_frame.ssh_pass == 'herring'
assert updated_frame.ssh_port == 2222
assert updated_frame.server_host == 'walrus'
assert updated_frame.server_port == 89999
assert updated_frame.server_port == 89898
assert updated_frame.device == 'framebuffer'
assert updated_frame.scaling_mode == 'contain'
assert updated_frame.rotate == 90
Expand Down Expand Up @@ -188,10 +188,10 @@ def test_api_frame_new(self):
assert response.status_code == 200
assert data['frame']['name'] == 'Frame'
assert data['frame']['frame_host'] == 'localhost'
assert data['frame']['frame_port'] == 8999
assert data['frame']['frame_port'] == 8787
assert data['frame']['ssh_port'] == 22
assert data['frame']['server_host'] == 'localhost'
assert data['frame']['server_port'] == 8999
assert data['frame']['server_port'] == 8989
assert data['frame']['device'] == 'web_only'

def test_api_frame_new_parsed(self):
Expand All @@ -200,12 +200,12 @@ def test_api_frame_new_parsed(self):
assert response.status_code == 200
assert data['frame']['name'] == 'Frame'
assert data['frame']['frame_host'] == 'localhost'
assert data['frame']['frame_port'] == 8999
assert data['frame']['frame_port'] == 8787
assert data['frame']['ssh_port'] == 22
assert data['frame']['ssh_user'] == 'user'
assert data['frame']['ssh_pass'] == 'pass'
assert data['frame']['server_host'] == 'localhost'
assert data['frame']['server_port'] == 8999
assert data['frame']['server_port'] == 8989
assert data['frame']['device'] == 'framebuffer'

def test_api_frame_delete(self):
Expand Down
Loading