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
 | 
			
		||||
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