Initial commit
This commit is contained in:
parent
15d8d45d27
commit
c84da24975
|
@ -0,0 +1,94 @@
|
|||
|
||||
FROM python:2.7
|
||||
EXPOSE 8080
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y cmake libjpeg62-turbo-dev g++ wget unzip psmisc
|
||||
|
||||
RUN cd /tmp/ && \
|
||||
wget https://github.com/jacksonliam/mjpg-streamer/archive/master.zip && \
|
||||
unzip master
|
||||
|
||||
RUN cd /tmp/mjpg-streamer-master/mjpg-streamer-experimental/ && \
|
||||
make && \
|
||||
make install
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
ENV CURA_VERSION=15.04.6
|
||||
ARG tag=master
|
||||
|
||||
WORKDIR /opt/octoprint
|
||||
|
||||
#install ffmpeg
|
||||
RUN cd /tmp \
|
||||
&& wget -O ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-32bit-static.tar.xz \
|
||||
&& mkdir -p /opt/ffmpeg \
|
||||
&& tar xvf ffmpeg.tar.xz -C /opt/ffmpeg --strip-components=1 \
|
||||
&& rm -Rf /tmp/*
|
||||
|
||||
#install Cura
|
||||
RUN cd /tmp \
|
||||
&& wget https://github.com/Ultimaker/CuraEngine/archive/${CURA_VERSION}.tar.gz \
|
||||
&& tar -zxf ${CURA_VERSION}.tar.gz \
|
||||
&& cd CuraEngine-${CURA_VERSION} \
|
||||
&& mkdir build \
|
||||
&& make \
|
||||
&& mv -f ./build /opt/cura/ \
|
||||
&& rm -Rf /tmp/*
|
||||
|
||||
#Create an octoprint user
|
||||
RUN useradd -ms /bin/bash octoprint && adduser octoprint dialout
|
||||
RUN chown octoprint:octoprint /opt/octoprint
|
||||
USER octoprint
|
||||
|
||||
#This fixes issues with the volume command setting wrong permissions
|
||||
RUN mkdir /home/octoprint/.octoprint
|
||||
|
||||
#Install Octoprint
|
||||
RUN git clone --branch $tag https://github.com/foosel/OctoPrint.git /opt/octoprint \
|
||||
&& virtualenv venv \
|
||||
&& ./venv/bin/python setup.py install \
|
||||
&& echo 2
|
||||
|
||||
RUN /opt/octoprint/venv/bin/python -m pip install https://github.com/FormerLurker/Octolapse/archive/master.zip && \
|
||||
/opt/octoprint/venv/bin/python -m pip install https://github.com/pablogventura/Octoprint-ETA/archive/master.zip && \
|
||||
/opt/octoprint/venv/bin/python -m pip install https://github.com/1r0b1n0/OctoPrint-Tempsgraph/archive/master.zip && \
|
||||
/opt/octoprint/venv/bin/python -m pip install https://github.com/dattas/OctoPrint-DetailedProgress/archive/master.zip && \
|
||||
/opt/octoprint/venv/bin/python -m pip install https://github.com/kennethjiang/OctoPrint-Slicer/archive/master.zip && \
|
||||
/opt/octoprint/venv/bin/python -m pip install https://github.com/marian42/octoprint-preheat/archive/master.zip && \
|
||||
/opt/octoprint/venv/bin/python -m pip install https://github.com/jneilliii/OctoPrint-TasmotaMQTT/archive/0.3.0.zip && \
|
||||
/opt/octoprint/venv/bin/python -m pip install https://github.com/mikedmor/OctoPrint_MultiCam/archive/master.zip
|
||||
|
||||
# Installing from sillyfrog until the PR is merged to master
|
||||
RUN /opt/octoprint/venv/bin/python -m pip install https://github.com/sillyfrog/Octoslacka/archive/master.zip && \
|
||||
/opt/octoprint/venv/bin/python -m pip install https://github.com/sillyfrog/OctoPrint-MQTT/archive/devel.zip
|
||||
|
||||
VOLUME /home/octoprint/.octoprint
|
||||
|
||||
|
||||
### Klipper setup ###
|
||||
|
||||
USER root
|
||||
|
||||
RUN apt-get install -y sudo
|
||||
|
||||
COPY klippy.sudoers /etc/sudoers.d/klippy
|
||||
|
||||
RUN useradd -ms /bin/bash klippy
|
||||
|
||||
USER octoprint
|
||||
|
||||
WORKDIR /home/octoprint
|
||||
|
||||
RUN git clone https://github.com/KevinOConnor/klipper
|
||||
|
||||
RUN ./klipper/scripts/install-octopi.sh
|
||||
|
||||
RUN cp klipper/config/printer-anet-a8-2017.cfg /home/octoprint/printer.cfg
|
||||
|
||||
USER root
|
||||
|
||||
COPY start.py /
|
||||
|
||||
CMD ["/start.py"]
|
31
README.md
31
README.md
|
@ -1,2 +1,31 @@
|
|||
# OctoPrint-Klipper-mjpg-Dockerfile
|
||||
A Dockerfile for running OctoPrint Klipper and mjpg in a single container
|
||||
A Dockerfile for running OctoPrint Klipper and mjpg in a single container.
|
||||
|
||||
My initial goal was to run these across different containers, but I couldn't get the Docker permissions to play nicely.
|
||||
|
||||
This is very much written for what I needed, so you'll likely need to hack this up for your setup. I've been using it for a little while now and it's going well.
|
||||
|
||||
Also included are some udev rules for reference that I use. These will need to be updated with your API key etc, however it makes connecting/disconnecting (power on/off) of the printer much less painful.
|
||||
|
||||
## Running the container
|
||||
|
||||
Once the container is built (the usual `docker build . -t okmd`), I use the following command to run it (again, you will need to customise for your setup, I have 3 cameras also connected):
|
||||
|
||||
```
|
||||
docker kill octoprint2
|
||||
docker rm octoprint2
|
||||
docker run --name octoprint2 -d -v /etc/localtime:/etc/localtime:ro -v /home/user/Documents/octoprint-config:/home/octoprint/.octoprint \
|
||||
--device /dev/ttyUSB0:/dev/ttyUSB0 \
|
||||
--device /dev/video0:/dev/video0 \
|
||||
--device /dev/video1:/dev/video1 \
|
||||
--device /dev/video2:/dev/video2 \
|
||||
-p 5000:5000 -p 8080:8080 -p 8081:8081 -p 8082:8082\
|
||||
-e "MJPG=input_uvc.so -r HD -d /dev/video2" \
|
||||
-e "MJPG1=input_uvc.so -r HD -d /dev/video0" -e "MJPG_PORT1=8081" \
|
||||
-e "MJPG2=input_uvc.so -r HD -d /dev/video1" -e "MJPG_PORT2=8082" \
|
||||
okmd
|
||||
```
|
||||
|
||||
Your Klipper `printer.cfg` should be kept in the OctoPrint config directory (this is where it looks for it at startup).
|
||||
|
||||
If you have any questions, feel free to log an issue on this project, and I'll see if I can help.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
octoprint ALL=(ALL:ALL) NOPASSWD: ALL
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import os
|
||||
import pwd
|
||||
|
||||
MJPG = [
|
||||
"/usr/local/bin/mjpg_streamer",
|
||||
"-i",
|
||||
"%(input)s",
|
||||
"-o",
|
||||
"output_http.so -p %(port)s -w /usr/local/share/mjpg-streamer/www/",
|
||||
]
|
||||
|
||||
MJPG_INPUT_DEFAULT = "input_uvc.so -r HD"
|
||||
|
||||
OCTOPRINT = ["/opt/octoprint/venv/bin/octoprint", "serve"]
|
||||
|
||||
|
||||
def main():
|
||||
mjpg_processes = []
|
||||
mjpg_ports = [5000] # Reserve the OctoPrint port
|
||||
for k, v in os.environ.iteritems():
|
||||
if k.startswith("MJPG") and not k.startswith("MJPG_"):
|
||||
v = v.strip()
|
||||
port_env = "MJPG_PORT" + k[4:]
|
||||
port = int(os.environ.get(port_env, 8080))
|
||||
if port in mjpg_ports:
|
||||
raise ValueError("Port %s from key %s already in use" % (port, port_env))
|
||||
if not v:
|
||||
v = MJPG_INPUT_DEFAULT
|
||||
subs = {'input': v, 'port': port}
|
||||
cmd = []
|
||||
for part in MJPG:
|
||||
cmd.append(part % subs)
|
||||
print("Starting: %s" % (cmd,))
|
||||
mjpg_processes.append(subprocess.Popen(cmd))
|
||||
|
||||
# Start klipper
|
||||
klipper = subprocess.Popen(['sudo', '-u', 'octoprint', '/home/octoprint/klippy-env/bin/python', '/home/octoprint/klipper/klippy/klippy.py', '/home/octoprint/.octoprint/printer.cfg'])
|
||||
|
||||
os.setgid(
|
||||
1000
|
||||
) # Drop privileges, https://stackoverflow.com/questions/2699907/dropping-root-permissions-in-python#2699996
|
||||
os.setuid(1000)
|
||||
os.environ['HOME'] = '/home/octoprint'
|
||||
# subprocess.Popen('env', shell=True).wait()
|
||||
while 1:
|
||||
Poctoprint = subprocess.Popen(OCTOPRINT)
|
||||
Poctoprint.wait()
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# udev Rules and Commands
|
||||
|
||||
This directory contains the commands and rules I add to udev to make turning the printer on and off go smoothly, including re-connecting (my Docker container runs 24x7, however I turn off the printer when not in use).
|
||||
|
||||
The `okmd-udev.rules` file should be copied to `/etc/udev/rules.d/`, and updated to have the correct paths and device names for your environment.
|
||||
|
||||
The commands (`printer-*`), should also be updated to match your device names, and include your OctoPrint API key.
|
|
@ -0,0 +1,2 @@
|
|||
ACTION=="add", KERNEL=="ttyUSB*", SUBSYSTEM=="tty", RUN+="/somewhere/printer-connected"
|
||||
ACTION=="remove", KERNEL=="ttyUSB*", SUBSYSTEM=="tty", RUN+="/somewhere/printer-disconnected"
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import time
|
||||
import os
|
||||
|
||||
logf = open('/var/tmp/printer-connected.log', 'a')
|
||||
|
||||
DEVNAME = os.environ['DEVNAME']
|
||||
|
||||
def log(msg):
|
||||
logf.write('%s\n' % (msg,))
|
||||
|
||||
def main():
|
||||
log(time.asctime())
|
||||
log(DEVNAME)
|
||||
os.system('chmod a+wr %s' % (DEVNAME,))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import time
|
||||
import os
|
||||
import requests
|
||||
import sys
|
||||
|
||||
logf = open('/var/tmp/printer-connected.log', 'a')
|
||||
|
||||
DEVNAME = os.environ['DEVNAME']
|
||||
|
||||
RESTART_STATES = [
|
||||
'Offline',
|
||||
'Cancelling',
|
||||
'Operational',
|
||||
]
|
||||
|
||||
OCTOPRINT_URL = 'http://localhost:5000/'
|
||||
API_KEY = 'XXX'
|
||||
|
||||
def log(msg):
|
||||
logf.write('%s\n' % (msg,))
|
||||
|
||||
def currentstate():
|
||||
headers = {'X-Api-Key': API_KEY}
|
||||
url = OCTOPRINT_URL + '/api/job'
|
||||
r = requests.get(url, headers=headers)
|
||||
return r.json()['state']
|
||||
|
||||
def main():
|
||||
log(time.asctime())
|
||||
log("Disconnecting: {}".format(DEVNAME))
|
||||
|
||||
state = currentstate()
|
||||
if state not in RESTART_STATES and DEVNAME != '/dev/ttyUSB0':
|
||||
log("Not restarting. State: {} DEVNAME: {}".format(state, DEVNAME))
|
||||
# Don't reset the firmware if not in a known safe state
|
||||
return
|
||||
|
||||
headers = {'X-Api-Key': API_KEY}
|
||||
json = {
|
||||
"command": "FIRMWARE_RESTART",
|
||||
}
|
||||
|
||||
url = OCTOPRINT_URL + '/api/printer/command'
|
||||
r = requests.post(
|
||||
url,
|
||||
json=json,
|
||||
headers=headers
|
||||
)
|
||||
|
||||
if (r.status_code == 204):
|
||||
log("Disconnected OK")
|
||||
sys.exit(0)
|
||||
else:
|
||||
log("Error disconnecting: %s" % (r,))
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
Loading…
Reference in New Issue