Initial commit
This commit is contained in:
		
							parent
							
								
									15d8d45d27
								
							
						
					
					
						commit
						c84da24975
					
				
					 8 changed files with 272 additions and 1 deletions
				
			
		
							
								
								
									
										94
									
								
								Dockerfile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								Dockerfile
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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
 | 
					# 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.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								klippy.sudoers
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								klippy.sudoers
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					octoprint   ALL=(ALL:ALL) NOPASSWD: ALL
 | 
				
			||||||
							
								
								
									
										57
									
								
								start.py
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										57
									
								
								start.py
									
										
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -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()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										7
									
								
								udev/README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								udev/README.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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.
 | 
				
			||||||
							
								
								
									
										2
									
								
								udev/okmd-udev.rules
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								udev/okmd-udev.rules
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					ACTION=="add", KERNEL=="ttyUSB*", SUBSYSTEM=="tty", RUN+="/somewhere/printer-connected"
 | 
				
			||||||
 | 
					ACTION=="remove", KERNEL=="ttyUSB*", SUBSYSTEM=="tty", RUN+="/somewhere/printer-disconnected"
 | 
				
			||||||
							
								
								
									
										20
									
								
								udev/printer-connected
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										20
									
								
								udev/printer-connected
									
										
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -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()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										61
									
								
								udev/printer-disconnected
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										61
									
								
								udev/printer-disconnected
									
										
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue