Skip to content

Commit ed89664

Browse files
committed
Add device orientation control web component and example
1 parent fa804b6 commit ed89664

File tree

4 files changed

+209
-0
lines changed

4 files changed

+209
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{% extends "base-controller.html" %}
2+
3+
{% block main %}
4+
<img src="video.mjpg" class="background-video"></img>
5+
6+
<div style="box-sizing: border-box; padding: 40px; display: flex;">
7+
{% if g.show_left_joystick %}
8+
<joystick-component
9+
onmove="publish({ type: 'left_joystick', data })"
10+
onend="publish({ type: 'left_joystick', data })"
11+
> </joystick-component>
12+
{% endif %}
13+
14+
<joystick-component
15+
onmove="publish({ type: 'right_joystick', data })"
16+
onend="publish({ type: 'right_joystick', data })"
17+
style="margin-left: auto"
18+
></joystick-component>
19+
</div>
20+
21+
<div style="box-sizing: border-box; padding: 40px; display: flex;">
22+
<orientation-component
23+
onchange="publish({ type: 'device_orientation', data })"
24+
></orientation-component>
25+
</div>
26+
{% endblock %}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import os
2+
3+
from pitop import Camera, DriveController, Pitop
4+
from pitop.labs import RoverWebController
5+
6+
# to access device orientation sensors, the webpage must be accessed over ssl,
7+
# so ensure that an ssl cert exists for this
8+
if not os.path.exists("cert.pem") or not os.path.exists("key.pem"):
9+
print("Generating self-signed ssl cert")
10+
os.system(
11+
'openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -nodes -subj "/O=pi-top"'
12+
)
13+
14+
rover = Pitop()
15+
rover.add_component(DriveController())
16+
rover.add_component(Camera())
17+
18+
19+
def device_orientation(data):
20+
print(data)
21+
x = data.get("x", 0)
22+
y = data.get("y", 0)
23+
24+
if abs(x) < 5:
25+
x = 0
26+
x = x * -0.1
27+
28+
if abs(y) < 5:
29+
y = 0
30+
y = y * 0.1
31+
32+
print(x, y)
33+
rover.drive.robot_move(y, x)
34+
35+
36+
rover_controller = RoverWebController(
37+
get_frame=rover.camera.get_frame,
38+
drive=rover.drive,
39+
message_handlers={"device_orientation": device_orientation},
40+
cert="cert.pem",
41+
key="key.pem",
42+
)
43+
44+
rover_controller.serve_forever()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
<script type="text/javascript" src="/webcomponents/vendor/nipplejs.min.js"></script>
22
<script type="text/javascript" src="/webcomponents/joystick-component.js"></script>
3+
<script type="text/javascript" src="/webcomponents/orientation-component.js"></script>
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
class DeviceOrientation extends HTMLElement {
2+
constructor() {
3+
super();
4+
}
5+
6+
connectedCallback() {
7+
if (!this.connected) {
8+
this.connected = true;
9+
this.setup();
10+
}
11+
}
12+
13+
disconnectedCallback() {
14+
this.disable();
15+
this.connected = false;
16+
}
17+
18+
setup = async () => {
19+
this.attachShadow({mode: 'open'});
20+
this.wrapper = document.createElement('div');
21+
this.shadowRoot.append(this.wrapper);
22+
23+
this.header = document.createElement('h3');
24+
this.header.textContent = 'Device Orientation Control';
25+
this.wrapper.appendChild(this.header);
26+
27+
if (typeof DeviceOrientationEvent === 'undefined') {
28+
this.header.textContent = 'Device Orientation Not Supported On This Device!';
29+
return;
30+
}
31+
32+
if (typeof DeviceOrientationEvent.requestPermission === 'function') {
33+
this.permissionButton = document.createElement('input');
34+
this.permissionButton.setAttribute('type','button');
35+
this.permissionButton.textContent('Allow sensor access');
36+
this.permissionButton.addEventListener('click', this.requestPermission);
37+
this.wrapper.appendChild(this.permissionButton);
38+
return;
39+
}
40+
41+
this.showEnable();
42+
}
43+
44+
requestPermission = async () => {
45+
const permissionState = await DeviceOrientationEvent.requestPermission()
46+
return permissionState === 'granted' ? this.permissionGranted() : this.permissionDenied();
47+
}
48+
49+
permissionGranted = () => {
50+
this.permissionButton.remove();
51+
this.showEnable();
52+
}
53+
54+
permissionDenied = () => {
55+
this.header.textContent = 'Device Orientation Permission Denied!';
56+
}
57+
58+
showEnable = () => {
59+
this.enabled = document.createElement('input');
60+
this.enabled.setAttribute('type','checkbox');
61+
this.enabled.setAttribute('id','enabled');
62+
this.wrapper.appendChild(this.enabled);
63+
64+
this.enabledLabel = document.createElement('label');
65+
this.enabledLabel.setAttribute('for','enabled');
66+
this.enabledLabel.innerText = 'Disabled';
67+
this.wrapper.appendChild(this.enabledLabel);
68+
69+
this.enabled.addEventListener('change', (event) => {
70+
if (event.currentTarget.checked) {
71+
this.enable();
72+
} else {
73+
this.disable();
74+
}
75+
})
76+
}
77+
78+
enable = () => {
79+
window.addEventListener('deviceorientation', this.handleOrientationEvent);
80+
// deviceorientation events don't fire when window blurs, so reset controls
81+
window.addEventListener('blur', this.orientationReset);
82+
83+
this.showOrientationDisplay();
84+
this.enabledLabel.innerText = 'Enabled';
85+
}
86+
87+
disable = () => {
88+
this.orientationReset();
89+
90+
window.removeEventListener('deviceorientation', this.handleOrientationEvent);
91+
window.removeEventListener('blur', this.orientationReset);
92+
93+
this.hideOrientationDisplay();
94+
this.enabledLabel.innerText = 'Disabled';
95+
}
96+
97+
orientationReset = () => {
98+
this.handleOrientationEvent({ alpha: 0, beta: 0, gamma: 0 });
99+
};
100+
101+
handleOrientationEvent = (event) => {
102+
const x = event.beta; // landscape left right
103+
const y = event.gamma; // landscape forward back
104+
const z = event.alpha; // landscape rotation
105+
106+
this.updateOrientationDisplay(x, y, z);
107+
108+
eval(`
109+
const data = ${JSON.stringify({ x, y, z })};
110+
${this.getAttribute('onchange')}
111+
`);
112+
}
113+
114+
showOrientationDisplay = () => {
115+
this.x = document.createElement('p');
116+
this.y = document.createElement('p');
117+
this.z = document.createElement('p');
118+
this.wrapper.appendChild(this.x);
119+
this.wrapper.appendChild(this.y);
120+
this.wrapper.appendChild(this.z);
121+
122+
this.updateOrientationDisplay(0, 0, 0)
123+
}
124+
125+
hideOrientationDisplay = () => {
126+
this.x.remove();
127+
this.y.remove();
128+
this.z.remove();
129+
}
130+
131+
updateOrientationDisplay = (x, y, z) => {
132+
this.x.textContent = 'x: ' + x;
133+
this.y.textContent = 'y: ' + y;
134+
this.z.textContent = 'z: ' + z;
135+
}
136+
}
137+
138+
window.customElements.define('orientation-component', DeviceOrientation);

0 commit comments

Comments
 (0)