├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── support.md
├── dependabot.yml
├── stale.yml
└── workflows
│ ├── approve-and-merge-dependency-updates.yaml
│ └── build.yaml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .stylelintignore
├── .stylelintrc.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── angular.json
├── docs
├── css
│ ├── icomoon.css
│ ├── ionicons.min.css
│ ├── style.css
│ └── theme.css
├── favicon.ico
├── fonts
│ ├── icomoon
│ │ ├── icomoon.eot
│ │ ├── icomoon.svg
│ │ ├── icomoon.ttf
│ │ └── icomoon.woff
│ └── ionicons
│ │ ├── css
│ │ ├── _ionicons.scss
│ │ └── ionicons.min.css
│ │ └── fonts
│ │ ├── ionicons.eot
│ │ ├── ionicons.svg
│ │ ├── ionicons.ttf
│ │ ├── ionicons.woff
│ │ └── ionicons.woff2
├── images
│ ├── icon-main-title.svg
│ ├── made-in-berlin.png
│ └── main.png
├── index.html
└── js
│ ├── lib
│ ├── aos.min.js
│ ├── bootstrap.min.js
│ ├── jquery-3.2.1.min.js
│ ├── jquery-migrate-3.0.1.min.js
│ ├── jquery.easing.1.3.js
│ ├── jquery.min.js
│ ├── jquery.stellar.min.js
│ ├── jquery.waypoints.min.js
│ ├── owl.carousel.min.js
│ └── scrollax.min.js
│ └── main.js
├── eslint.config.js
├── main.js
├── package-lock.json
├── package.json
├── screenshots
├── adjust_parameters_print.png
├── adjust_temperatures_main_screen.png
├── babystep_z.png
├── cancel.png
├── control.png
├── control_confirmation.png
├── error_message.png
├── filament_extruding.png
├── filament_heating.png
├── filament_purging.png
├── filament_selection.png
├── file_details.png
├── file_loaded.png
├── files_view.png
├── job.png
├── main-screen.png
├── no_job_no_touchscreen.png
├── paused.png
├── print_controls.png
├── settings.png
└── sleeping.png
├── scripts
├── install.sh
├── remove.sh
└── update.sh
├── src
├── app.module.ts
├── app.routing.module.ts
├── assets
│ ├── adjust.svg
│ ├── animations
│ │ ├── checkmark.json
│ │ ├── loading.json
│ │ └── toggle-switch.json
│ ├── cancel.svg
│ ├── connect.svg
│ ├── control.svg
│ ├── fan.svg
│ ├── filament.svg
│ ├── folder.svg
│ ├── fonts
│ │ ├── Arvo-Bold.ttf
│ │ ├── Cousine-Regular.ttf
│ │ ├── Montserrat-Medium.ttf
│ │ └── Montserrat-Regular.ttf
│ ├── heat-bed.svg
│ ├── heat.svg
│ ├── height.svg
│ ├── icon
│ │ ├── icon-alt-dark-title.svg
│ │ ├── icon-alt-dark.svg
│ │ ├── icon-alt-title.svg
│ │ ├── icon-alt.svg
│ │ ├── icon-main-dark-title.svg
│ │ ├── icon-main-dark.svg
│ │ ├── icon-main-title.svg
│ │ ├── icon-main.svg
│ │ └── icon.png
│ ├── invalid-config.svg
│ ├── made-in-berlin.png
│ ├── nozzle.svg
│ ├── object.svg
│ ├── pause.svg
│ └── resume.svg
├── components
│ ├── action-center
│ │ ├── action-center.component.html
│ │ ├── action-center.component.scss
│ │ └── action-center.component.ts
│ ├── app.component.html
│ ├── app.component.scss
│ ├── app.component.ts
│ ├── control
│ │ ├── control.component.html
│ │ ├── control.component.scss
│ │ └── control.component.ts
│ ├── filament
│ │ ├── change-filament
│ │ │ ├── change-filament.component.html
│ │ │ ├── change-filament.component.scss
│ │ │ └── change-filament.component.ts
│ │ ├── choose-filament
│ │ │ ├── choose-filament.component.html
│ │ │ ├── choose-filament.component.scss
│ │ │ └── choose-filament.component.ts
│ │ ├── choose-tool
│ │ │ ├── choose-tool.component.html
│ │ │ ├── choose-tool.component.scss
│ │ │ └── choose-tool.component.ts
│ │ ├── filament.component.html
│ │ ├── filament.component.scss
│ │ ├── filament.component.ts
│ │ ├── heat-nozzle
│ │ │ ├── heat-nozzle.component.html
│ │ │ ├── heat-nozzle.component.scss
│ │ │ └── heat-nozzle.component.ts
│ │ ├── move-filament
│ │ │ ├── move-filament.component.html
│ │ │ ├── move-filament.component.scss
│ │ │ └── move-filament.component.ts
│ │ └── purge-filament
│ │ │ ├── purge-filament.component.html
│ │ │ ├── purge-filament.component.scss
│ │ │ └── purge-filament.component.ts
│ ├── files
│ │ ├── files.component.html
│ │ ├── files.component.scss
│ │ └── files.component.ts
│ ├── index.ts
│ ├── main-screen
│ │ ├── bottom-bar
│ │ │ ├── bottom-bar.component.html
│ │ │ ├── bottom-bar.component.scss
│ │ │ └── bottom-bar.component.ts
│ │ ├── job-status
│ │ │ ├── height-progress
│ │ │ │ ├── height-progress.component.html
│ │ │ │ ├── height-progress.component.scss
│ │ │ │ └── height-progress.component.ts
│ │ │ ├── job-status.component.html
│ │ │ ├── job-status.component.scss
│ │ │ └── job-status.component.ts
│ │ ├── main-menu
│ │ │ ├── main-menu.component.html
│ │ │ ├── main-menu.component.scss
│ │ │ └── main-menu.component.ts
│ │ ├── main-screen.component.html
│ │ ├── main-screen.component.ts
│ │ ├── print-control
│ │ │ ├── print-control.component.html
│ │ │ ├── print-control.component.scss
│ │ │ └── print-control.component.ts
│ │ └── printer-status
│ │ │ ├── printer-status.component.html
│ │ │ ├── printer-status.component.scss
│ │ │ └── printer-status.component.ts
│ ├── notification
│ │ ├── notification.component.html
│ │ ├── notification.component.scss
│ │ └── notification.component.ts
│ ├── reset
│ │ ├── reset.component.html
│ │ ├── reset.component.scss
│ │ └── reset.component.ts
│ ├── settings
│ │ ├── settings-icon
│ │ │ ├── settings-icon.component.html
│ │ │ ├── settings-icon.component.scss
│ │ │ └── settings-icon.component.ts
│ │ ├── settings.component.html
│ │ ├── settings.component.scss
│ │ └── settings.component.ts
│ ├── setup
│ │ ├── discover-octoprint
│ │ │ ├── discover-octoprint.component.html
│ │ │ ├── discover-octoprint.component.scss
│ │ │ └── discover-octoprint.component.ts
│ │ ├── extruder-information
│ │ │ ├── extruder-information.component.html
│ │ │ ├── extruder-information.component.scss
│ │ │ └── extruder-information.component.ts
│ │ ├── invalid-config
│ │ │ ├── invalid-config.component.html
│ │ │ ├── invalid-config.component.scss
│ │ │ └── invalid-config.component.ts
│ │ ├── octoprint-authentication
│ │ │ ├── octoprint-authentication.component.html
│ │ │ ├── octoprint-authentication.component.scss
│ │ │ ├── octoprint-authentication.component.ts
│ │ │ └── octoprint-authentication.service.ts
│ │ ├── personalization
│ │ │ ├── personalization.component.html
│ │ │ ├── personalization.component.scss
│ │ │ ├── personalization.component.ts
│ │ │ └── personalization.service.ts
│ │ ├── plugins
│ │ │ ├── plugins.component.html
│ │ │ ├── plugins.component.scss
│ │ │ └── plugins.component.ts
│ │ ├── setup.component.html
│ │ ├── setup.component.scss
│ │ ├── setup.component.ts
│ │ └── welcome
│ │ │ ├── welcome.component.html
│ │ │ ├── welcome.component.scss
│ │ │ └── welcome.component.ts
│ ├── shared
│ │ ├── hotend-icon
│ │ │ ├── hotend-icon.component.html
│ │ │ ├── hotend-icon.component.scss
│ │ │ └── hotend-icon.component.ts
│ │ ├── quick-control
│ │ │ ├── quick-control.component.html
│ │ │ ├── quick-control.component.scss
│ │ │ └── quick-control.component.ts
│ │ ├── toggle-switch
│ │ │ ├── toggle-switch.component.html
│ │ │ ├── toggle-switch.component.scss
│ │ │ └── toggle-switch.component.ts
│ │ └── top-bar
│ │ │ ├── top-bar.component.html
│ │ │ ├── top-bar.component.scss
│ │ │ └── top-bar.component.ts
│ ├── standby
│ │ ├── standby.component.html
│ │ ├── standby.component.scss
│ │ └── standby.component.ts
│ └── update
│ │ ├── update.component.html
│ │ ├── update.component.scss
│ │ └── update.component.ts
├── directives
│ ├── index.ts
│ └── long-press.directive.ts
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── helper
│ ├── config.js
│ ├── config.schema.json
│ ├── discover.js
│ ├── listener.js
│ ├── locale.js
│ ├── protocol.js
│ ├── styles.js
│ └── update.js
├── index.html
├── locale
│ ├── messages.da.xlf
│ ├── messages.de.xlf
│ ├── messages.fr.xlf
│ └── messages.xlf
├── main.ts
├── model
│ ├── auth.model.ts
│ ├── enclosure.model.ts
│ ├── event.model.ts
│ ├── filament.model.ts
│ ├── files.model.ts
│ ├── index.ts
│ ├── job.model.ts
│ ├── octoprint
│ │ ├── auth.model.ts
│ │ ├── connection.model.ts
│ │ ├── file.model.ts
│ │ ├── index.ts
│ │ ├── job.model.ts
│ │ ├── plugins
│ │ │ ├── companion.model.ts
│ │ │ ├── display-layer-progress.model.ts
│ │ │ ├── enclosure.model.ts
│ │ │ ├── filament-manager.model.ts
│ │ │ ├── ophomplugstatus.model.ts
│ │ │ ├── psucontrol.model.ts
│ │ │ ├── spool-manager.model.ts
│ │ │ ├── tasmota-mqtt.model.ts
│ │ │ ├── tasmota.model.ts
│ │ │ ├── tp-link.model.ts
│ │ │ ├── tuya.model.ts
│ │ │ └── wemo.model.ts
│ │ ├── printer-commands.model.ts
│ │ ├── printer-profile.model.ts
│ │ └── socket.model.ts
│ ├── printer-profile.model.ts
│ ├── printer.model.ts
│ ├── system.model.ts
│ └── util-test.model.ts
├── pipes
│ ├── index.ts
│ └── url.pipe.ts
├── reset.css
├── services
│ ├── app.service.ts
│ ├── config.service.ts
│ ├── conversion.service.ts
│ ├── electron.service.ts
│ ├── enclosure
│ │ ├── enclosure.octoprint.service.ts
│ │ └── enclosure.service.ts
│ ├── event.service.ts
│ ├── filament
│ │ ├── filament-manager.octoprint.service.ts
│ │ ├── filament-plugin.service.ts
│ │ ├── filament.service.ts
│ │ └── spool-manager.octoprint.service.ts
│ ├── files
│ │ ├── files.octoprint.service.ts
│ │ └── files.service.ts
│ ├── index.ts
│ ├── job
│ │ ├── job.octoprint.service.ts
│ │ └── job.service.ts
│ ├── notification.service.ts
│ ├── printer
│ │ ├── printer.octoprint.service.ts
│ │ └── printer.service.ts
│ ├── socket
│ │ ├── socket.octoprint.service.ts
│ │ └── socket.service.ts
│ └── system
│ │ ├── system.octoprint.service.ts
│ │ └── system.service.ts
└── styles.scss
├── themes
├── NOX
│ ├── README.md
│ ├── custom-styles.css
│ └── screenshots
│ │ ├── screenshot_file-loaded.png
│ │ ├── screenshot_file.png
│ │ ├── screenshot_files.png
│ │ ├── screenshot_main-screen.png
│ │ ├── screenshot_printing1.png
│ │ └── screenshot_printing2.png
├── dark-nights
│ ├── README.md
│ ├── custom-styles.css
│ └── screenshots
│ │ ├── dashboard.png
│ │ ├── filament-purge.png
│ │ ├── filament-temp.png
│ │ ├── file-browser.png
│ │ ├── file-preview.png
│ │ ├── file-selection.png
│ │ ├── printing-adjust.png
│ │ ├── printing-percent.png
│ │ ├── printing-preview.png
│ │ └── temp-adjust.png
├── square
│ ├── README.md
│ └── custom-styles.css
└── theGarbz
│ ├── BigFingers
│ ├── README.md
│ ├── custom-styles.css
│ └── screenshots
│ │ ├── icon.png
│ │ ├── screenshot_filaments.png
│ │ ├── screenshot_files.png
│ │ └── screenshot_settings.png
│ ├── Focus
│ ├── README.md
│ ├── custom-styles.css
│ └── screenshots
│ │ ├── icon.png
│ │ ├── screenshot_adjust.png
│ │ ├── screenshot_error.png
│ │ ├── screenshot_filequeue.png
│ │ ├── screenshot_fileselect.png
│ │ ├── screenshot_main.png
│ │ ├── screenshot_menu.png
│ │ ├── screenshot_print.png
│ │ └── screenshot_print2.png
│ ├── Glanceable
│ ├── README.md
│ ├── custom-styles.css
│ └── screenshots
│ │ ├── icon.png
│ │ ├── screenshot_printing_circle.png
│ │ └── screenshot_printing_straight.png
│ └── README.md
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: https://paypal.me/TimonGaebelein
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | ---
7 |
8 | **Describe the bug**
9 | A clear and concise description of what the bug is.
10 |
11 | **To Reproduce**
12 | Steps to reproduce the behavior:
13 |
14 | 1. Go to '...'
15 | 2. Click on '....'
16 | 3. Scroll down to '....'
17 | 4. See error
18 |
19 | **Expected behavior**
20 | A clear and concise description of what you expected to happen.
21 |
22 | **Screenshots**
23 | If applicable, add screenshots to help explain your problem.
24 |
25 | **General Information:**
26 |
27 | - Hardware [e.g. Raspberry Pi, if you have layout issues please also include your screen resolution]
28 | - OS Info [e.g. Raspbian Buster Lite, please also indicate if you used OctoPi]
29 | - OctoDash Version [e.g. v1.0.0]
30 | - OctoPrint Version [e.g. v1.0.0]
31 |
32 | **Additional context**
33 | Add any other context about the problem here.
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 | ---
8 |
9 | **Is your feature request related to a problem? Please describe.**
10 | A clear and concise description of what the problem is. If applicable also link other issues / bugs here.
11 |
12 | **Describe the solution you'd like**
13 | A clear and concise description of what you want to happen.
14 |
15 | **Link other projects**
16 | If you want OctoDash to include other OctoPrint plugins or similar please link them here.
17 |
18 | **Additional context**
19 | Add any other context or screenshots about the feature request here.
20 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/support.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Support
3 | about: Something is not working or you have a question about your setup
4 | title: ''
5 | labels: support
6 | assignees: ''
7 | ---
8 |
9 | **What doesn't work?**
10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
11 |
12 | **What did you already try?**
13 | A clear and concise description of what you tried to make OctoDash work.
14 |
15 | **General Information:**
16 |
17 | - Hardware [e.g. Raspberry Pi, if you have layout issues please also include your screen resolution]
18 | - OS Info [e.g. Raspbian Buster Lite, please also indicate if you used OctoPi]
19 | - OctoDash Version [e.g. v1.0.0]
20 | - OctoPrint Version [e.g. v1.0.0]
21 |
22 | **Additional context**
23 | Add any other context or screenshots about the feature request here.
24 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: '/'
5 | schedule:
6 | interval: daily
7 | time: '10:00'
8 | open-pull-requests-limit: 15
9 | labels:
10 | - dependencies
11 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 14
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 2
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | - enhancement
10 | - bug
11 | - documentation
12 | - code
13 | # Label to use when marking an issue as stale
14 | staleLabel: stale
15 | # Comment to post when marking an issue as stale. Set to `false` to disable
16 | markComment: >
17 | This issue has been automatically marked as stale because it has not had
18 | recent activity. It will be closed if no further activity occurs. Thank you
19 | for your contributions.
20 | # Comment to post when closing a stale issue. Set to `false` to disable
21 | closeComment: false
22 |
--------------------------------------------------------------------------------
/.github/workflows/approve-and-merge-dependency-updates.yaml:
--------------------------------------------------------------------------------
1 | name: Dependencies
2 |
3 | on:
4 | pull_request_target:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | auto-approve:
10 | name: Approve
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Auto Approve
14 | uses: hmarr/auto-approve-action@v4
15 | if: github.actor == 'dependabot[bot]'
16 | with:
17 | github-token: '${{ secrets.GITHUB_TOKEN }}'
18 | auto-merge:
19 | name: Merge
20 | needs: auto-approve
21 | runs-on: ubuntu-latest
22 | steps:
23 | - name: Auto Merge
24 | uses: pascalgn/automerge-action@v0.16.4
25 | if: github.actor == 'dependabot[bot]'
26 | env:
27 | GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
28 | MERGE_METHOD: 'squash'
29 | MERGE_LABELS: 'dependencies'
30 | MERGE_RETRIES: 15
31 | MERGE_RETRY_SLEEP: 60000
32 |
--------------------------------------------------------------------------------
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 | workflow_dispatch:
11 |
12 | jobs:
13 | lint:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - name: Checkout Repository
18 | uses: actions/checkout@v4
19 | - name: Cache node modules
20 | uses: actions/cache@v4
21 | env:
22 | cache-name: node-modules
23 | with:
24 | path: '**/node_modules'
25 | key: octodash-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
26 | restore-keys: |
27 | octodash-build-${{ env.cache-name }}-
28 | - name: Use Node.js
29 | uses: actions/setup-node@v4
30 | with:
31 | node-version: '22'
32 | - name: Install latest npm
33 | run: sudo npm install -g npm@latest
34 | - name: Installing Dependencies
35 | run: npm ci
36 | - name: Linting Application
37 | run: npm run lint
38 | - name: Checking if locale is updated
39 | run: |-
40 | npm run locale:extract
41 | if [ "$(git diff --name-only)" ]; then
42 | echo ""
43 | echo "ERROR! Locale file update detected! Please run 'npm run locale:update'."
44 | echo ""
45 | exit 1
46 | fi
47 |
48 | build:
49 | runs-on: ubuntu-latest
50 |
51 | steps:
52 | - name: Checkout Repository
53 | uses: actions/checkout@v4
54 | - name: Cache node modules
55 | uses: actions/cache@v4
56 | env:
57 | cache-name: node-modules
58 | with:
59 | path: '**/node_modules'
60 | key: octodash-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
61 | restore-keys: |
62 | octodash-build-${{ env.cache-name }}-
63 | - name: Use Node.js
64 | uses: actions/setup-node@v4
65 | with:
66 | node-version: '22'
67 | - name: Install latest npm
68 | run: sudo npm install -g npm@latest
69 | - name: Installing Dependencies
70 | run: npm ci
71 | - name: Building Application
72 | run: npm run build
73 | - name: Packaging Application
74 | run: npm run electron:pack
75 | env:
76 | GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
77 | - name: Upload artifacts
78 | uses: actions/upload-artifact@v4
79 | with:
80 | name: build
81 | path: |
82 | package/*.deb
83 | package/*.yaml
84 | package/*.yml
85 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # System Files
4 | .DS_Store
5 | Thumbs.db
6 |
7 | # Generated files
8 | yarn.lock
9 | .angular
10 | npm-debug.log
11 | yarn-error.log
12 | testem.log
13 | **/config.testing.json
14 | src/locale/backups
15 |
16 | # compiled output
17 | /dist
18 | /package
19 | /tmp
20 | /out-tsc
21 | # Only exists if Bazel was run
22 | /bazel-out
23 |
24 | # dependencies
25 | **/node_modules
26 |
27 | # profiling files
28 | chrome-profiler-events.json
29 | speed-measure-plugin.json
30 |
31 | # IDEs and editors
32 | /.idea
33 | .project
34 | .classpath
35 | .c9/
36 | *.launch
37 | .settings/
38 | *.sublime-workspace
39 |
40 | # IDE - VSCode
41 | .vscode
42 | .vscode/*
43 | !.vscode/settings.json
44 | !.vscode/tasks.json
45 | !.vscode/launch.json
46 | !.vscode/extensions.json
47 | .history/*
48 |
49 | # misc
50 | /.sass-cache
51 | /connect.lock
52 | /coverage
53 | /libpeerconnection.log
54 | /typings
55 |
56 |
57 | docs/images/compilation.psd
58 | scripts/clean-github.sh
59 | src/model/config.model.ts
60 | src/helper/config.default.json
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | LICENSE.md
2 | *.min.*
3 | build
4 | dist
5 |
6 | docs/css/icomoon.css
7 | docs/css/theme.css
8 | src/reset.css
9 | src/assets/animations/
10 | docs/fonts/ionicons
11 | docs/js/lib/
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "useTabs": false,
4 | "arrowParens": "avoid",
5 | "bracketSpacing": true,
6 | "endOfLine": "lf",
7 | "bracketSameLine": true,
8 | "printWidth": 120,
9 | "semi": true,
10 | "singleQuote": true,
11 | "trailingComma": "all",
12 | "htmlWhitespaceSensitivity": "css"
13 | }
14 |
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | dist/
2 | *.min.*
3 | docs/css/icomoon.css
4 | docs/css/theme.css
5 | docs/fonts/ionicons/
6 | src/reset.css
--------------------------------------------------------------------------------
/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["stylelint-config-standard-scss"],
3 | "rules": {
4 | "selector-class-pattern": null,
5 | "selector-id-pattern": null,
6 | "no-descending-specificity": null,
7 | "selector-pseudo-element-no-unknown": [
8 | true,
9 | {
10 | "ignorePseudoElements": ["ng-deep"]
11 | }
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/docs/css/style.css:
--------------------------------------------------------------------------------
1 | .main-icon {
2 | width: 35%;
3 | margin-bottom: 6%;
4 | margin-top: 30%;
5 | }
6 |
7 | .icon-github::before {
8 | line-height: 190%;
9 | }
10 |
11 | .main-section {
12 | height: 95vh;
13 | }
14 |
15 | .navbar-brand {
16 | font-family: Arvo, sans-serif;
17 | font-weight: 600;
18 | }
19 |
20 | .btn {
21 | font-size: 150%;
22 | }
23 |
24 | .first-heading-title {
25 | margin-top: 10%;
26 | }
27 |
28 | .heading-title {
29 | margin-top: 3%;
30 | }
31 |
32 | p {
33 | font-size: 119%;
34 | }
35 |
36 | h5 {
37 | text-align: center;
38 | font-size: 150%;
39 | line-height: 300%;
40 | }
41 |
42 | .install-script {
43 | text-align: center;
44 | font-size: 120%;
45 | font-family: Cousine, monospace;
46 | background-color: #252932;
47 | padding: 2vh;
48 | border-radius: 8px;
49 | margin-bottom: 0.5vh;
50 | display: block;
51 | white-space: nowrap;
52 | width: auto;
53 | overflow-y: scroll;
54 | }
55 |
56 | .carousel-item {
57 | background-size: contain;
58 | margin-top: 1.5%;
59 | margin-bottom: 3.5%;
60 | }
61 |
62 | .carousel-inner {
63 | background-color: #252932;
64 | }
65 |
66 | h4 {
67 | float: left;
68 | margin-right: 15px;
69 | }
70 |
71 | .nav-item > a {
72 | line-height: 250%;
73 | }
74 |
75 | .nav-pills > li {
76 | width: 100%;
77 | }
78 |
79 | .main-button {
80 | padding-bottom: 1rem !important;
81 | }
82 |
83 | .subtitle {
84 | width: 100%;
85 | }
86 |
87 | .myaccordion .btn {
88 | text-transform: none;
89 | text-align: left;
90 | }
91 |
92 | .iframe-wrapper {
93 | position: relative;
94 | overflow: hidden;
95 | padding-top: 40%;
96 | }
97 |
98 | .iframe {
99 | position: absolute;
100 | top: 0;
101 | left: 0;
102 | width: 100%;
103 | height: 100%;
104 | border: 0;
105 | }
106 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/fonts/icomoon/icomoon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/docs/fonts/icomoon/icomoon.eot
--------------------------------------------------------------------------------
/docs/fonts/icomoon/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/docs/fonts/icomoon/icomoon.ttf
--------------------------------------------------------------------------------
/docs/fonts/icomoon/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/docs/fonts/icomoon/icomoon.woff
--------------------------------------------------------------------------------
/docs/fonts/ionicons/fonts/ionicons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/docs/fonts/ionicons/fonts/ionicons.eot
--------------------------------------------------------------------------------
/docs/fonts/ionicons/fonts/ionicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/docs/fonts/ionicons/fonts/ionicons.ttf
--------------------------------------------------------------------------------
/docs/fonts/ionicons/fonts/ionicons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/docs/fonts/ionicons/fonts/ionicons.woff
--------------------------------------------------------------------------------
/docs/fonts/ionicons/fonts/ionicons.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/docs/fonts/ionicons/fonts/ionicons.woff2
--------------------------------------------------------------------------------
/docs/images/made-in-berlin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/docs/images/made-in-berlin.png
--------------------------------------------------------------------------------
/docs/images/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/docs/images/main.png
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | import path from 'node:path';
2 | import { fileURLToPath } from 'node:url';
3 |
4 | import electron from 'electron';
5 |
6 | import activateListeners from './src/helper/listener.js';
7 | import { getLocale } from './src/helper/locale.js';
8 | import createProtocol from './src/helper/protocol.js';
9 |
10 | const { app, BrowserWindow, ipcMain, protocol, screen } = electron;
11 |
12 | let window;
13 | let locale;
14 | let url;
15 |
16 | const dev = !!process.env.APP_DEV;
17 | const __dirname = fileURLToPath(new URL('.', import.meta.url));
18 |
19 | if (!dev) {
20 | const scheme = 'app';
21 |
22 | protocol.registerSchemesAsPrivileged([{ scheme: scheme, privileges: { standard: true } }]);
23 | createProtocol(scheme, path.join(__dirname, 'dist'));
24 |
25 | locale = getLocale();
26 | }
27 |
28 | // Fixes rendering glitches on Raspberry Pi + Electron v27+
29 | app.disableHardwareAcceleration();
30 |
31 | app.commandLine.appendSwitch('touch-events', 'enabled');
32 |
33 | function createWindow() {
34 | // if (!dev) {
35 | // session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
36 | // callback({
37 | // responseHeaders: {
38 | // ...details.responseHeaders,
39 | // "Content-Security-Policy": ["script-src 'self'"],
40 | // },
41 | // });
42 | // });
43 | // }
44 |
45 | const mainScreen = screen.getPrimaryDisplay();
46 |
47 | window = new BrowserWindow({
48 | width: dev ? 1100 : mainScreen.size.width,
49 | height: dev ? 600 : mainScreen.size.height,
50 | frame: dev,
51 | backgroundColor: '#353b48',
52 | webPreferences: {
53 | nodeIntegration: true,
54 | enableRemoteModule: true,
55 | contextIsolation: false,
56 | },
57 | icon: fileURLToPath(new URL(`./${dev ? 'src' : 'dist'}/assets/icon/icon.png`, import.meta.url)),
58 | });
59 |
60 | if (dev) {
61 | url = 'http://localhost:4200';
62 | let devtools = new BrowserWindow();
63 | window.webContents.setDevToolsWebContents(devtools.webContents);
64 | window.webContents.openDevTools({ mode: 'detach' });
65 | } else {
66 | url = `file://${__dirname}/dist/${locale}/index.html`;
67 | window.setFullScreen(true);
68 | }
69 |
70 | window.loadURL(url);
71 | activateListeners(ipcMain, window, app, url);
72 |
73 | window.on('closed', () => {
74 | window = null;
75 | });
76 | }
77 |
78 | app.on('ready', createWindow);
79 |
80 | app.on('window-all-closed', () => {
81 | app.quit();
82 | });
83 |
84 | app.on('activate', () => {
85 | if (window === null) {
86 | createWindow();
87 | }
88 | });
89 |
--------------------------------------------------------------------------------
/screenshots/adjust_parameters_print.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/adjust_parameters_print.png
--------------------------------------------------------------------------------
/screenshots/adjust_temperatures_main_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/adjust_temperatures_main_screen.png
--------------------------------------------------------------------------------
/screenshots/babystep_z.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/babystep_z.png
--------------------------------------------------------------------------------
/screenshots/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/cancel.png
--------------------------------------------------------------------------------
/screenshots/control.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/control.png
--------------------------------------------------------------------------------
/screenshots/control_confirmation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/control_confirmation.png
--------------------------------------------------------------------------------
/screenshots/error_message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/error_message.png
--------------------------------------------------------------------------------
/screenshots/filament_extruding.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/filament_extruding.png
--------------------------------------------------------------------------------
/screenshots/filament_heating.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/filament_heating.png
--------------------------------------------------------------------------------
/screenshots/filament_purging.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/filament_purging.png
--------------------------------------------------------------------------------
/screenshots/filament_selection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/filament_selection.png
--------------------------------------------------------------------------------
/screenshots/file_details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/file_details.png
--------------------------------------------------------------------------------
/screenshots/file_loaded.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/file_loaded.png
--------------------------------------------------------------------------------
/screenshots/files_view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/files_view.png
--------------------------------------------------------------------------------
/screenshots/job.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/job.png
--------------------------------------------------------------------------------
/screenshots/main-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/main-screen.png
--------------------------------------------------------------------------------
/screenshots/no_job_no_touchscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/no_job_no_touchscreen.png
--------------------------------------------------------------------------------
/screenshots/paused.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/paused.png
--------------------------------------------------------------------------------
/screenshots/print_controls.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/print_controls.png
--------------------------------------------------------------------------------
/screenshots/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/settings.png
--------------------------------------------------------------------------------
/screenshots/sleeping.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/screenshots/sleeping.png
--------------------------------------------------------------------------------
/scripts/remove.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "Removing OctoDash ..."
4 | killall octodash
5 |
6 | sudo dpkg -P octodash
7 |
8 | rm -rf ~/.config/octodash/
9 |
10 | sed -i '/xset s off/d' ~/.xinitrc
11 | sed -i '/xset s noblank/d' ~/.xinitrc
12 | sed -i '/xset -dpms/d' ~/.xinitrc
13 | sed -i '/ratpoison&/d' ~/.xinitrc
14 | sed -i '/octodash --no-sandbox/d' ~/.xinitrc
15 |
16 | echo "OctoDash has been removed :(. Please review your ~/.xinitrc and ~/.bashrc files to make sure everything got removed properly!"
17 |
--------------------------------------------------------------------------------
/scripts/update.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ ! -f "/etc/debian_version" ]; then
4 | echo ""
5 | echo "OctoDash is only compatible with Debian-based Linux installations!"
6 | echo ""
7 | fi
8 |
9 | arch=$(dpkg --print-architecture)
10 | if [[ $arch == armhf ]]; then
11 | releaseURL=$(curl -s "https://api.github.com/repos/UnchartedBull/OctoDash/releases/latest" | grep "browser_download_url.*armv7l.deb" | cut -d '"' -f 4)
12 | elif [[ $arch == arm64 ]]; then
13 | releaseURL=$(curl -s "https://api.github.com/repos/UnchartedBull/OctoDash/releases/latest" | grep "browser_download_url.*arm64.deb" | cut -d '"' -f 4)
14 | elif [[ $arch == amd64 ]]; then
15 | releaseURL=$(curl -s "https://api.github.com/repos/UnchartedBull/OctoDash/releases/latest" | grep "browser_download_url.*amd64.deb" | cut -d '"' -f 4)
16 | fi
17 |
18 | echo "Updating OctoDash"
19 |
20 | cd ~
21 |
22 | wget -O octodash.deb $releaseURL -q --show-progress
23 |
24 | sudo dpkg -i octodash.deb
25 |
26 | rm octodash.deb
27 |
28 | echo "Done. Restart your Raspberry Pi to start the newest version!"
29 |
--------------------------------------------------------------------------------
/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import { DragDropModule } from '@angular/cdk/drag-drop';
2 | import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
3 | import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
4 | import { FormsModule } from '@angular/forms';
5 | import { MatRippleModule } from '@angular/material/core';
6 | import { BrowserModule } from '@angular/platform-browser';
7 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
8 | import { FaIconLibrary, FontAwesomeModule } from '@fortawesome/angular-fontawesome';
9 | import { fas } from '@fortawesome/free-solid-svg-icons';
10 | import { RoundProgressModule } from 'angular-svg-round-progressbar';
11 | import lottiePlayer from 'lottie-web';
12 | import { LottieComponent, provideCacheableAnimationLoader, provideLottieOptions } from 'ngx-lottie';
13 |
14 | import { AppRoutingModule } from './app.routing.module';
15 | import components from './components';
16 | import { AppComponent } from './components/app.component';
17 | import directives from './directives';
18 | import pipes from './pipes';
19 | import services from './services';
20 |
21 | @NgModule({
22 | declarations: [...components, ...directives, ...pipes],
23 | bootstrap: [AppComponent],
24 | schemas: [CUSTOM_ELEMENTS_SCHEMA],
25 | imports: [
26 | AppRoutingModule,
27 | BrowserAnimationsModule,
28 | BrowserModule,
29 | DragDropModule,
30 | FontAwesomeModule,
31 | FormsModule,
32 | MatRippleModule,
33 | RoundProgressModule,
34 | LottieComponent,
35 | ],
36 | providers: [
37 | ...services,
38 | [provideLottieOptions({ player: () => lottiePlayer })],
39 | [provideCacheableAnimationLoader()],
40 | provideHttpClient(withInterceptorsFromDi()),
41 | ],
42 | })
43 | export class AppModule {
44 | public constructor(library: FaIconLibrary) {
45 | library.addIconPacks(fas);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/app.routing.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { RouterModule, Routes } from '@angular/router';
4 |
5 | import { ControlComponent } from './components/control/control.component';
6 | import { FilamentComponent } from './components/filament/filament.component';
7 | import { FilesComponent } from './components/files/files.component';
8 | import { MainScreenComponent } from './components/main-screen/main-screen.component';
9 | import { SettingsComponent } from './components/settings/settings.component';
10 | import { ConfigInvalidComponent } from './components/setup/invalid-config/invalid-config.component';
11 | import { ConfigSetupComponent } from './components/setup/setup.component';
12 | import { StandbyComponent } from './components/standby/standby.component';
13 |
14 | const routes: Routes = [
15 | {
16 | path: 'main-screen',
17 | component: MainScreenComponent,
18 | },
19 | {
20 | path: 'control',
21 | component: ControlComponent,
22 | },
23 | {
24 | path: 'filament',
25 | component: FilamentComponent,
26 | },
27 | {
28 | path: 'files',
29 | component: FilesComponent,
30 | },
31 | {
32 | path: 'invalid-config',
33 | component: ConfigInvalidComponent,
34 | },
35 | {
36 | path: 'no-config',
37 | component: ConfigSetupComponent,
38 | },
39 | {
40 | path: 'settings',
41 | component: SettingsComponent,
42 | },
43 | {
44 | path: 'standby',
45 | component: StandbyComponent,
46 | },
47 | ];
48 |
49 | @NgModule({
50 | declarations: [],
51 | imports: [CommonModule, RouterModule.forRoot(routes, { useHash: true, enableViewTransitions: true })],
52 | exports: [RouterModule],
53 | })
54 | export class AppRoutingModule {}
55 |
--------------------------------------------------------------------------------
/src/assets/adjust.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/cancel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/connect.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/control.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/fan.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/filament.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/folder.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/fonts/Arvo-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/src/assets/fonts/Arvo-Bold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Cousine-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/src/assets/fonts/Cousine-Regular.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Montserrat-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/src/assets/fonts/Montserrat-Medium.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Montserrat-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/src/assets/fonts/Montserrat-Regular.ttf
--------------------------------------------------------------------------------
/src/assets/heat-bed.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/assets/heat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/height.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/icon/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/src/assets/icon/icon.png
--------------------------------------------------------------------------------
/src/assets/invalid-config.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/made-in-berlin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/src/assets/made-in-berlin.png
--------------------------------------------------------------------------------
/src/assets/nozzle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/object.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/pause.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/resume.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
{{ status }}
14 |
Initializing is taking longer than usual...
16 | Please make sure that OctoPrint is running and that CORS is enabled for the API.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
31 |
32 |
37 |
38 |
39 |
40 |
45 |
46 |
--------------------------------------------------------------------------------
/src/components/app.component.scss:
--------------------------------------------------------------------------------
1 | .container {
2 | width: 100%;
3 | height: 100%;
4 | display: block;
5 | position: relative;
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/filament/change-filament/change-filament.component.html:
--------------------------------------------------------------------------------
1 |
2 | load new filament into tool {{ selectedTool }}
3 | only put a little filament in, I'll pull in the rest.
6 |
7 |
8 |
9 |
10 |
11 | {{ selectedSpool.material }}
16 |
17 |
{{ selectedSpool.displayName }}
18 |
{{ getSpoolWeightLeft(selectedSpool.weight, selectedSpool.used)
20 | }}g left
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 | I'll wait for you.
34 |
35 |
36 |
37 |
38 | M600 sent
39 | follow the instructions on your printer's display
42 |
43 |
--------------------------------------------------------------------------------
/src/components/filament/change-filament/change-filament.component.scss:
--------------------------------------------------------------------------------
1 | .change-filament {
2 | &__info {
3 | font-size: 60%;
4 | opacity: 0.8;
5 | font-style: italic;
6 | display: block;
7 | text-align: center;
8 | }
9 |
10 | &__filament {
11 | &-name {
12 | text-align: center;
13 | font-size: 0.8rem;
14 | opacity: 0.7;
15 | display: block;
16 | text-overflow: ellipsis;
17 | white-space: nowrap;
18 | }
19 |
20 | &-weight {
21 | opacity: 1;
22 | font-size: 1rem;
23 | line-height: 1.5rem;
24 | display: block;
25 | text-align: center;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/filament/change-filament/change-filament.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
2 |
3 | import { FilamentSpool } from '../../../model';
4 | import { ConfigService } from '../../../services/config.service';
5 | import { PrinterService } from '../../../services/printer/printer.service';
6 |
7 | @Component({
8 | selector: 'app-filament-change-filament',
9 | templateUrl: './change-filament.component.html',
10 | styleUrls: [
11 | './change-filament.component.scss',
12 | '../filament.component.scss',
13 | '../heat-nozzle/heat-nozzle.component.scss',
14 | ],
15 | standalone: false,
16 | })
17 | export class ChangeFilamentComponent implements OnInit {
18 | @Input() selectedSpool: FilamentSpool;
19 | @Input() selectedTool: number;
20 |
21 | @Output() increasePage = new EventEmitter();
22 |
23 | constructor(
24 | private configService: ConfigService,
25 | private printerService: PrinterService,
26 | ) {}
27 |
28 | ngOnInit(): void {
29 | if (this.configService.useM600()) {
30 | this.initiateM600FilamentChange();
31 | } else {
32 | this.disableExtruderStepper();
33 | }
34 | }
35 |
36 | private disableExtruderStepper(): void {
37 | this.printerService.executeGCode(`${this.configService.getDisableExtruderGCode()}`);
38 | }
39 |
40 | private initiateM600FilamentChange(): void {
41 | this.printerService.executeGCode(`M600 T${this.selectedTool}`);
42 | }
43 |
44 | public getSpoolWeightLeft(weight: number, used: number): number {
45 | return Math.floor(weight - used);
46 | }
47 |
48 | public usingM600(): boolean {
49 | return this.configService.useM600();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/components/filament/choose-filament/choose-filament.component.html:
--------------------------------------------------------------------------------
1 | select your new filament
2 |
3 |
4 |
5 |
15 |
16 |
17 |
18 |
19 | {{ spool.material }}
20 |
21 |
22 | {{ spool.displayName }}
23 |
24 |
25 | {{ getSpoolWeightLeft(spool.weight, spool.used)
26 | }}g left
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | no filament spools found
35 |
36 |
37 |
38 | loading spools
39 |
40 |
--------------------------------------------------------------------------------
/src/components/filament/choose-filament/choose-filament.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Output } from '@angular/core';
2 |
3 | import { FilamentSpool } from '../../../model';
4 | import { FilamentService } from '../../../services/filament/filament.service';
5 |
6 | @Component({
7 | selector: 'app-filament-choose-spool',
8 | templateUrl: './choose-filament.component.html',
9 | styleUrls: ['./choose-filament.component.scss', '../filament.component.scss'],
10 | standalone: false,
11 | })
12 | export class ChooseFilamentComponent {
13 | @Output() spoolChange = new EventEmitter<{ spool: FilamentSpool; skipChange: boolean }>();
14 |
15 | private currentSpools: number[];
16 |
17 | constructor(public filament: FilamentService) {
18 | this.currentSpools = (filament.getCurrentSpools() || []).map(s => s.id);
19 | }
20 |
21 | public getSpoolWeightLeft(weight: number, used: number): number {
22 | return Math.floor(weight - used);
23 | }
24 |
25 | public setSpool(spool: FilamentSpool): void {
26 | setTimeout(() => {
27 | this.spoolChange.emit({ spool, skipChange: false });
28 | }, 150);
29 | }
30 |
31 | public setSpoolSkipChange(spool: FilamentSpool): void {
32 | setTimeout(() => {
33 | this.spoolChange.emit({ spool, skipChange: true });
34 | }, 150);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/filament/choose-tool/choose-tool.component.html:
--------------------------------------------------------------------------------
1 | select tool
2 |
14 |
--------------------------------------------------------------------------------
/src/components/filament/choose-tool/choose-tool.component.scss:
--------------------------------------------------------------------------------
1 | .tools {
2 | margin-top: 2vh;
3 | height: auto;
4 | display: block;
5 | overflow-y: scroll;
6 | scrollbar-width: none;
7 | padding-right: 1.5vw;
8 |
9 | tr td {
10 | background-color: var(--background-3);
11 | border-radius: 1vw;
12 | padding: 0 1vw;
13 | display: block;
14 | width: 92vw;
15 | margin-left: 2vw;
16 | margin-bottom: 2vh;
17 | transition: color 0.25s;
18 | box-sizing: border-box;
19 | border: 0;
20 | position: relative;
21 | text-align: center;
22 | font-size: 6vw;
23 | font-weight: 500;
24 |
25 | &:first-of-type {
26 | margin-top: 3.5vh;
27 | }
28 |
29 | &:last-of-type {
30 | margin-bottom: 3.5vh;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/filament/choose-tool/choose-tool.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Output } from '@angular/core';
2 | import { take } from 'rxjs/operators';
3 |
4 | import { PrinterStatus } from '../../../model';
5 | import { PrinterService } from '../../../services/printer/printer.service';
6 | import { SocketService } from '../../../services/socket/socket.service';
7 |
8 | @Component({
9 | selector: 'app-filament-choose-tool',
10 | templateUrl: './choose-tool.component.html',
11 | styleUrls: ['./choose-tool.component.scss', '../filament.component.scss'],
12 | standalone: false,
13 | })
14 | export class ChooseToolComponent {
15 | public toolCount = 1;
16 |
17 | @Output() toolChange = new EventEmitter();
18 |
19 | public constructor(
20 | private printerService: PrinterService,
21 | private socketService: SocketService,
22 | ) {
23 | this.socketService
24 | .getPrinterStatusSubscribable()
25 | .pipe(take(1))
26 | .subscribe((printerStatus: PrinterStatus): void => {
27 | this.toolCount = printerStatus.tools.length;
28 | });
29 | }
30 |
31 | public setTool(tool: number): void {
32 | setTimeout(() => {
33 | this.toolChange.emit(tool);
34 | }, 150);
35 | }
36 |
37 | //function to return list of numbers from 0 to n-1
38 | numSequence(n: number): Array {
39 | return Array(n);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/filament/filament.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
13 |
14 |
20 |
25 |
30 |
36 |
40 |
41 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/components/filament/filament.component.scss:
--------------------------------------------------------------------------------
1 | .filament {
2 | padding: 2vh 2vw 0;
3 |
4 | &__progress-bar {
5 | height: 4vh;
6 | border-radius: 2vh;
7 | background-color: var(--success);
8 | width: 0;
9 | transition: width 0.7s ease-in-out;
10 |
11 | &-wrapper {
12 | background-color: var(--background-3);
13 | height: 4vh;
14 | width: 20vw;
15 | display: inline-block;
16 | border-radius: 2vh;
17 |
18 | &-wide {
19 | width: 50vw;
20 | margin: 14vh auto 3vh;
21 | display: block;
22 | height: 7vh;
23 | background-color: transparent;
24 | border: 3px solid var(--border);
25 | border-radius: 3vh;
26 |
27 | > div {
28 | height: 7vh;
29 | width: 50vw;
30 | background-color: transparent;
31 | }
32 | }
33 | }
34 | }
35 |
36 | &-heading {
37 | display: block;
38 | text-align: center;
39 | font-size: 1rem;
40 | }
41 |
42 | &__wrapper-button {
43 | text-align: center;
44 | position: absolute;
45 | width: 96vw;
46 | bottom: 5vh;
47 | }
48 |
49 | &__done {
50 | display: inline-block;
51 | background-color: var(--success);
52 | padding: 2vh 2vw;
53 | border-radius: 8px;
54 | }
55 | }
56 |
57 | .checkmark {
58 | margin-top: 10vh;
59 | height: 80vh;
60 | display: block;
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/filament/heat-nozzle/heat-nozzle.component.html:
--------------------------------------------------------------------------------
1 | heating the nozzle for tool {{ selectedTool }}
2 |
3 |
4 |
5 |
6 | +1
7 |
8 |
9 | +10
10 |
11 |
12 |
13 | {{ isHeating ? hotendTemperature : hotendTarget }}°C
14 | /{{ hotendTarget }}°C
15 |
16 |
17 |
18 | -1
19 |
20 |
21 | -10
22 |
23 |
24 |
25 |
42 |
43 |
44 |
45 | wait
46 | {{ automaticHeatingStartSeconds }}
47 | s or
48 |
49 |
start
57 |
58 |
--------------------------------------------------------------------------------
/src/components/filament/move-filament/move-filament.component.html:
--------------------------------------------------------------------------------
1 | {{ action === 'load' ? loadingMessage : unloadingMessage }}
3 | filament
5 |
11 | {{ feedSpeed }}mm/s
12 |
22 |
--------------------------------------------------------------------------------
/src/components/filament/move-filament/move-filament.component.scss:
--------------------------------------------------------------------------------
1 | .move-filament {
2 | &__speed {
3 | font-size: 60%;
4 | opacity: 0.8;
5 | font-style: italic;
6 | display: block;
7 | text-align: center;
8 | }
9 |
10 | &__cancel {
11 | display: inline-block;
12 | background-color: var(--error);
13 | padding: 2vh 2vw;
14 | border-radius: 8px;
15 | }
16 |
17 | &__progress-bar {
18 | height: 4vh;
19 | border-radius: 2vh;
20 | background-color: var(--success);
21 | width: 0;
22 | transition: width 0.7s ease-in-out;
23 |
24 | &-wrapper {
25 | width: 50vw;
26 | margin: 14vh auto 3vh;
27 | display: block;
28 | height: 7vh;
29 | background-color: transparent;
30 | border: 3px solid var(--border);
31 | border-radius: 3vh;
32 |
33 | > div {
34 | height: 7vh;
35 | width: 50vw;
36 | background-color: transparent;
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/filament/purge-filament/purge-filament.component.html:
--------------------------------------------------------------------------------
1 | purging filament
2 | {{ purgeAmount }}mm
3 |
21 |
--------------------------------------------------------------------------------
/src/components/filament/purge-filament/purge-filament.component.scss:
--------------------------------------------------------------------------------
1 | .purge-filament {
2 | &__amount {
3 | text-align: center;
4 | margin-top: 12vh;
5 | font-size: 250%;
6 | }
7 |
8 | &__more,
9 | &__done {
10 | display: inline-block;
11 | padding: 2vh 2vw;
12 | border-radius: 8px;
13 | }
14 |
15 | &__more {
16 | background-color: #7f8fa6;
17 | margin-right: 10vw;
18 | }
19 |
20 | &__done {
21 | background-color: var(--success);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/filament/purge-filament/purge-filament.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
2 |
3 | import { ConfigService } from '../../../services/config.service';
4 | import { PrinterService } from '../../../services/printer/printer.service';
5 |
6 | @Component({
7 | selector: 'app-filament-purge-filament',
8 | templateUrl: './purge-filament.component.html',
9 | styleUrls: ['./purge-filament.component.scss', '../filament.component.scss'],
10 | standalone: false,
11 | })
12 | export class PurgeFilamentComponent implements OnInit {
13 | @Input() selectedTool: number;
14 |
15 | @Output() purgeDone = new EventEmitter();
16 |
17 | public purgeAmount: number;
18 |
19 | constructor(
20 | private configService: ConfigService,
21 | private printerService: PrinterService,
22 | ) {}
23 |
24 | ngOnInit(): void {
25 | this.purgeAmount = this.configService.useM600() ? 0 : this.configService.getPurgeDistance();
26 | if (this.purgeAmount === 0) {
27 | this.purgeDone.emit();
28 | } else {
29 | this.purgeFilament(this.purgeAmount);
30 | }
31 | }
32 |
33 | public increasePurgeAmount(length: number): void {
34 | this.purgeAmount += length;
35 | this.purgeFilament(length);
36 | }
37 |
38 | public purgeFilament(length: number): void {
39 | this.printerService.extrude(length, this.configService.getFeedSpeedSlow(), this.selectedTool);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/main-screen/bottom-bar/bottom-bar.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ getPrinterName() }}
4 |
5 |
6 | {{ enclosureTemperature.temperature }}{{ enclosureTemperature.unit }}
7 |
8 |
9 | {{ statusText }}
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/main-screen/bottom-bar/bottom-bar.component.scss:
--------------------------------------------------------------------------------
1 | .bottom-bar {
2 | position: absolute;
3 | bottom: 2.5vh;
4 | left: 2vw;
5 | width: calc(100% - 5.6vw);
6 |
7 | td {
8 | font-size: 4vw;
9 | }
10 |
11 | &__printer-name {
12 | text-overflow: ellipsis;
13 | }
14 |
15 | &__enclosure-temperature {
16 | text-align: center;
17 | width: 20%;
18 |
19 | &-icon {
20 | width: 2.5vw;
21 | display: inline-block;
22 | margin-bottom: -0.8vh;
23 | margin-right: 1vw;
24 | }
25 | }
26 |
27 | &__current-status {
28 | text-align: right;
29 | }
30 |
31 | &__error {
32 | color: var(--error);
33 | animation: blinker 2s linear infinite;
34 | }
35 | }
36 |
37 | @keyframes blinker {
38 | 20% {
39 | opacity: 1;
40 | }
41 |
42 | 50% {
43 | opacity: 0;
44 | }
45 |
46 | 80% {
47 | opacity: 1;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/main-screen/job-status/height-progress/height-progress.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Z
4 |
5 | {{ height }}
6 |
7 | mm
8 |
9 |
10 |
11 |
12 | Layer
17 |
18 | {{ height.current }}
19 |
20 | of
21 |
22 | {{ height.total >= 0 ? height.total : '---' }}
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/components/main-screen/job-status/height-progress/height-progress.component.scss:
--------------------------------------------------------------------------------
1 | .height-indication {
2 | width: 100%;
3 | display: block;
4 | margin-top: 1vh;
5 | text-align: center;
6 | font-size: 4vw;
7 |
8 | &__current-height {
9 | font-size: 6vw;
10 | font-weight: 500;
11 | }
12 |
13 | &__z {
14 | border: 3px solid white;
15 | padding: 0 0.2rem;
16 | font-size: 0.5rem;
17 | font-weight: bold;
18 | border-radius: 0.1rem;
19 | vertical-align: 0.05rem;
20 | display: inline-block;
21 | margin-right: 1.5vw;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/main-screen/job-status/height-progress/height-progress.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnDestroy, OnInit } from '@angular/core';
2 | import { Subscription } from 'rxjs';
3 |
4 | import { ZHeightLayer } from '../../../../model';
5 | import { SocketService } from '../../../../services/socket/socket.service';
6 |
7 | @Component({
8 | selector: 'app-height-progress',
9 | templateUrl: './height-progress.component.html',
10 | styleUrls: ['./height-progress.component.scss'],
11 | standalone: false,
12 | })
13 | export class HeightProgressComponent implements OnInit, OnDestroy {
14 | private subscriptions: Subscription = new Subscription();
15 | public height: number | ZHeightLayer;
16 |
17 | public constructor(private socketService: SocketService) {}
18 |
19 | public ngOnInit(): void {
20 | this.subscriptions.add(
21 | this.socketService.getJobStatusSubscribable().subscribe(jobStatus => {
22 | this.height = jobStatus.zHeight;
23 | }),
24 | );
25 | }
26 |
27 | public ngOnDestroy(): void {
28 | this.subscriptions.unsubscribe();
29 | }
30 |
31 | public isNumber(variable: number | ZHeightLayer): boolean {
32 | return typeof variable === 'number';
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/main-screen/job-status/job-status.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnDestroy, OnInit } from '@angular/core';
2 | import { Subscription } from 'rxjs';
3 |
4 | import { JobStatus } from '../../../model';
5 | import { ConfigService } from '../../../services/config.service';
6 | import { EventService } from '../../../services/event.service';
7 | import { FilesService } from '../../../services/files/files.service';
8 | import { JobService } from '../../../services/job/job.service';
9 | import { SocketService } from '../../../services/socket/socket.service';
10 |
11 | @Component({
12 | selector: 'app-job-status',
13 | templateUrl: './job-status.component.html',
14 | styleUrls: ['./job-status.component.scss'],
15 | standalone: false,
16 | })
17 | export class JobStatusComponent implements OnInit, OnDestroy {
18 | private subscriptions: Subscription = new Subscription();
19 |
20 | public jobStatus: JobStatus;
21 | public thumbnail: string;
22 | public showPreviewWhilePrinting: boolean;
23 |
24 | public constructor(
25 | private jobService: JobService,
26 | private fileService: FilesService,
27 | private socketService: SocketService,
28 | private eventService: EventService,
29 | private configService: ConfigService,
30 | ) {
31 | this.showPreviewWhilePrinting = this.configService.showThumbnailByDefault();
32 | }
33 |
34 | public ngOnInit(): void {
35 | this.subscriptions.add(
36 | this.socketService.getJobStatusSubscribable().subscribe((jobStatus: JobStatus): void => {
37 | if (jobStatus.file !== this.jobStatus?.file) {
38 | this.fileService.getThumbnail(jobStatus.fullPath).subscribe(thumbnail => {
39 | this.thumbnail = thumbnail;
40 | });
41 | }
42 | this.jobStatus = jobStatus;
43 | }),
44 | );
45 | }
46 |
47 | public ngOnDestroy(): void {
48 | this.subscriptions.unsubscribe();
49 | }
50 |
51 | public isFileLoaded(): boolean {
52 | return this.fileService.getLoadedFile();
53 | }
54 |
55 | public isPreheatEnabled(): boolean {
56 | return this.configService.isPreheatPluginEnabled();
57 | }
58 |
59 | public preheat(): void {
60 | this.jobService.preheat();
61 | }
62 |
63 | public discardLoadedFile(): void {
64 | this.fileService.setLoadedFile(false);
65 | }
66 |
67 | public startJob(): void {
68 | this.jobService.startJob();
69 | setTimeout((): void => {
70 | this.fileService.setLoadedFile(false);
71 | }, 5000);
72 | }
73 |
74 | public isPrinting(): boolean {
75 | return this.eventService.isPrinting();
76 | }
77 |
78 | public togglePreview(): void {
79 | this.showPreviewWhilePrinting = !this.showPreviewWhilePrinting;
80 | }
81 |
82 | public hasProperty(object: Record, name: string): boolean {
83 | return Object.hasOwnProperty.bind(object)(name);
84 | }
85 |
86 | public useCircularProgressBar(): boolean {
87 | return this.configService.getPreviewProgressCircle();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/components/main-screen/main-menu/main-menu.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ now | date: 'HH:mm' }}
4 | OctoDash.
5 |
6 |
7 |
8 |
9 |
10 | files
11 |
12 |
13 |
14 | filament
15 |
16 |
17 |
18 | control
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/components/main-screen/main-menu/main-menu.component.scss:
--------------------------------------------------------------------------------
1 | .main-menu {
2 | display: block;
3 | width: 100%;
4 | height: 58vh;
5 |
6 | &__heading {
7 | display: block;
8 | text-align: center;
9 | font-size: 5vw;
10 | font-weight: 500;
11 | margin-top: 4vh;
12 | font-family: Arvo, sans-serif;
13 |
14 | &__dot {
15 | color: var(--primary);
16 | opacity: 1;
17 | animation: breathe 5s ease-in-out infinite alternate;
18 | }
19 | }
20 |
21 | &__options {
22 | margin: auto;
23 | width: 90vw;
24 |
25 | & td {
26 | width: 29.66vw;
27 | display: inline-block;
28 | text-align: center;
29 | font-weight: 500;
30 | }
31 | }
32 |
33 | &__option-icon {
34 | display: block;
35 | padding-bottom: 4vh;
36 | margin: auto;
37 |
38 | &__1 {
39 | height: 25vh;
40 | margin-bottom: -1.3vh;
41 | }
42 |
43 | &__2 {
44 | height: 22vh;
45 | }
46 |
47 | &__3 {
48 | height: 19vh;
49 | margin-bottom: 1.6vh;
50 | }
51 | }
52 | }
53 |
54 | @keyframes breathe {
55 | 0% {
56 | opacity: 1;
57 | }
58 |
59 | 100% {
60 | opacity: 0.5;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/components/main-screen/main-menu/main-menu.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnDestroy } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-main-menu',
5 | templateUrl: './main-menu.component.html',
6 | styleUrls: ['./main-menu.component.scss'],
7 | standalone: false,
8 | })
9 | export class MainMenuComponent implements OnDestroy {
10 | public now = Date.now();
11 | public interval;
12 |
13 | public constructor() {
14 | this.interval = setInterval(() => (this.now = Date.now()), 1);
15 | }
16 |
17 | public ngOnDestroy(): void {
18 | clearInterval(this.interval);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/main-screen/main-screen.component.html:
--------------------------------------------------------------------------------
1 |
8 |
13 |
--------------------------------------------------------------------------------
/src/components/main-screen/main-screen.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { Router } from '@angular/router';
3 |
4 | import { ConfigService } from '../../services/config.service';
5 | import { EventService } from '../../services/event.service';
6 | import { FilesService } from '../../services/files/files.service';
7 |
8 | @Component({
9 | selector: 'app-main-screen',
10 | templateUrl: './main-screen.component.html',
11 | standalone: false,
12 | })
13 | export class MainScreenComponent {
14 | public printing = false;
15 |
16 | public constructor(
17 | private eventService: EventService,
18 | private fileService: FilesService,
19 | private configService: ConfigService,
20 | private router: Router,
21 | ) {
22 | if (!this.configService.isInitialized()) {
23 | this.router.navigate(['/']);
24 | }
25 | }
26 |
27 | public isPrinting(): boolean {
28 | return this.eventService.isPrinting();
29 | }
30 |
31 | public isFileLoaded(): boolean {
32 | return this.fileService.getLoadedFile();
33 | }
34 |
35 | public isTouchscreen(): boolean {
36 | return this.configService.isTouchscreen();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/main-screen/printer-status/printer-status.component.scss:
--------------------------------------------------------------------------------
1 | .printer-status {
2 | width: 95vw;
3 | margin: auto 3vh;
4 | height: 27vh;
5 | display: grid;
6 | grid-template-rows: repeat(1, 1fr);
7 | grid-template-columns: 1fr 2fr;
8 | gap: 1vw 2vw;
9 | place-items: center start;
10 | align-content: center;
11 |
12 | &.printer-status__multi-hotend {
13 | grid-template-rows: repeat(2, 1fr);
14 | grid-template-columns: repeat(1, 1fr);
15 |
16 | .printer-status__row {
17 | grid-column: 1 / -1;
18 | }
19 | }
20 |
21 | .printer-status__row {
22 | display: grid;
23 | width: 100%;
24 | grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
25 | column-gap: 2vw;
26 | place-items: center start;
27 | align-content: center;
28 |
29 | &:last-child {
30 | grid-template-columns: repeat(2, 1fr);
31 | }
32 |
33 | .printer-status__item {
34 | width: 100%;
35 |
36 | .printer-status__icon {
37 | vertical-align: middle;
38 | display: inline-block;
39 | width: 6vw;
40 | }
41 |
42 | .printer-status__value {
43 | display: inline-block;
44 | width: calc(100% - 6vw);
45 | text-align: right;
46 | vertical-align: middle;
47 | line-height: 6vh;
48 |
49 | &.printer-status__small-text span {
50 | font-size: 2.5vw;
51 | }
52 |
53 | .printer-status__unit {
54 | font-size: 60%;
55 | }
56 |
57 | .printer-status__actual-value {
58 | font-size: 5vw;
59 | font-weight: 500;
60 | }
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/main-screen/printer-status/printer-status.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnDestroy, OnInit } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { Subscription } from 'rxjs';
4 |
5 | import { PrinterExtruders, PrinterProfile, PrinterStatus } from '../../../model';
6 | import { ConfigService } from '../../../services/config.service';
7 | import { PrinterService } from '../../../services/printer/printer.service';
8 | import { SocketService } from '../../../services/socket/socket.service';
9 |
10 | @Component({
11 | selector: 'app-printer-status',
12 | templateUrl: './printer-status.component.html',
13 | styleUrls: ['./printer-status.component.scss'],
14 | standalone: false,
15 | })
16 | export class PrinterStatusComponent implements OnInit, OnDestroy {
17 | private subscriptions: Subscription = new Subscription();
18 | public printerStatus: PrinterStatus;
19 | public extruderInfo: PrinterExtruders = {
20 | count: 1,
21 | offsets: [],
22 | sharedNozzle: false,
23 | };
24 | public fanSpeed: number;
25 | public status: string;
26 |
27 | public selectedHotend: number;
28 | public sharedNozzle: boolean;
29 |
30 | public QuickControlView = QuickControlView;
31 | public view = QuickControlView.NONE;
32 |
33 | public constructor(
34 | private printerService: PrinterService,
35 | private configService: ConfigService,
36 | private socketService: SocketService,
37 | private router: Router,
38 | ) {}
39 |
40 | public ngOnInit(): void {
41 | this.subscriptions.add(
42 | this.printerService.getActiveProfile().subscribe({
43 | next: (printerProfile: PrinterProfile) => (this.extruderInfo = printerProfile.extruder),
44 | }),
45 | );
46 | this.subscriptions.add(
47 | this.socketService.getPrinterStatusSubscribable().subscribe((status: PrinterStatus): void => {
48 | this.printerStatus = status;
49 | }),
50 | );
51 | }
52 |
53 | public ngOnDestroy(): void {
54 | this.subscriptions.unsubscribe();
55 | }
56 |
57 | public showQuickControlHotend(tool: number): void {
58 | this.view = QuickControlView.HOTEND;
59 | this.selectedHotend = tool;
60 | }
61 |
62 | public showQuickControlHeatbed(): void {
63 | this.view = QuickControlView.HEATBED;
64 | }
65 |
66 | public showQuickControlFan(): void {
67 | this.view = QuickControlView.FAN;
68 | }
69 |
70 | public hideQuickControl(): void {
71 | this.view = QuickControlView.NONE;
72 | }
73 |
74 | public quickControlSetValue(value: number): void {
75 | switch (this.view) {
76 | case QuickControlView.HOTEND:
77 | this.printerService.setTemperatureHotend(value, this.selectedHotend);
78 | break;
79 | case QuickControlView.HEATBED:
80 | this.printerService.setTemperatureBed(value);
81 | break;
82 | case QuickControlView.FAN:
83 | this.printerService.setFanSpeed(value);
84 | break;
85 | }
86 |
87 | this.hideQuickControl();
88 | }
89 | }
90 |
91 | enum QuickControlView {
92 | NONE,
93 | HOTEND,
94 | HEATBED,
95 | FAN,
96 | }
97 |
--------------------------------------------------------------------------------
/src/components/notification/notification.component.html:
--------------------------------------------------------------------------------
1 |
5 |
{{ notification?.time | date: 'HH:mm' }}
6 |
{{ notification?.heading }}
7 |
{{ notification?.text }}
8 |
tap this card to close it
9 |
10 |
15 | {{ choice }}
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/components/notification/notification.component.scss:
--------------------------------------------------------------------------------
1 | .notification {
2 | display: block;
3 | position: fixed;
4 | width: 80vw;
5 | background-color: var(--background-2);
6 | border-radius: 1.3vw;
7 | left: 10vw;
8 | top: -75vh;
9 | transition: top 0.7s ease-in-out;
10 | box-shadow: 0 4px 10px -3px rgb(0 0 0 / 75%);
11 | z-index: 100;
12 | border-right: 1vw solid var(--background-2);
13 |
14 | &-prompt {
15 | &__choices {
16 | display: flex;
17 | flex-wrap: wrap;
18 | justify-content: space-evenly;
19 | }
20 |
21 | &__choice {
22 | display: block;
23 | font-size: 2.7vw;
24 | background-color: var(--primary);
25 | text-align: center;
26 | padding: 3vh;
27 | border-radius: 0.8vw;
28 | margin: 1vw;
29 | flex-grow: 100;
30 |
31 | &-first {
32 | background-color: var(--primary);
33 | }
34 | }
35 | }
36 |
37 | &__show {
38 | top: 5vh;
39 | }
40 |
41 | &__heading {
42 | display: block;
43 | text-align: center;
44 | font-size: 3.5vw;
45 | font-weight: 500;
46 | margin-top: 4vh;
47 | }
48 |
49 | &__text {
50 | font-size: 2.7vw;
51 | padding: 5vh 3vw 1vh;
52 | display: block;
53 | text-align: center;
54 | }
55 |
56 | &__close {
57 | font-size: 2vw;
58 | display: block;
59 | text-align: center;
60 | padding-bottom: 2vh;
61 | opacity: 0.4;
62 | }
63 |
64 | &__time {
65 | position: absolute;
66 | right: 0.3vw;
67 | top: 0.6vw;
68 | font-size: 2.4vw;
69 | opacity: 0.6;
70 | }
71 |
72 | &__border {
73 | &-3 {
74 | border-left: 1vw solid #a1abb7;
75 | }
76 |
77 | &-2 {
78 | border-left: 1vw solid #c23616;
79 | }
80 |
81 | &-1 {
82 | border-left: 1vw solid #fbc531;
83 | }
84 |
85 | &-0 {
86 | border-left: 1vw solid #4bae50;
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/components/notification/notification.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, NgZone, OnDestroy } from '@angular/core';
2 | import { Subscription } from 'rxjs';
3 |
4 | import { Notification } from '../../model';
5 | import { NotificationService } from '../../services/notification.service';
6 |
7 | @Component({
8 | selector: 'app-notification',
9 | templateUrl: './notification.component.html',
10 | styleUrls: ['./notification.component.scss'],
11 | standalone: false,
12 | })
13 | export class NotificationComponent implements OnDestroy {
14 | private subscriptions: Subscription = new Subscription();
15 |
16 | public notification?: Notification;
17 | public notificationCloseTimeout: ReturnType;
18 | public show = false;
19 |
20 | public constructor(
21 | private notificationService: NotificationService,
22 | private zone: NgZone,
23 | ) {
24 | this.subscriptions.add(
25 | this.notificationService
26 | .getObservable()
27 | .subscribe((notification: Notification | 'close'): void => this.setNotification(notification)),
28 | );
29 | }
30 |
31 | public hideNotification(removeFromStack = true, userTriggered = false): void {
32 | if (!userTriggered || (userTriggered && !this.notification.choices)) {
33 | this.show = false;
34 | clearTimeout(this.notificationCloseTimeout);
35 | if (removeFromStack) this.notificationService.removeNotification(this.notification);
36 | }
37 | }
38 |
39 | public chooseAction(index: number, callback: (index: number) => void): void {
40 | callback(index);
41 | this.hideNotification();
42 | }
43 |
44 | private setNotification(notification: Notification | 'close'): void {
45 | this.zone.run(() => {
46 | if (notification === 'close') {
47 | this.hideNotification();
48 | } else {
49 | this.hideNotification(false);
50 | this.notification = notification;
51 | this.show = true;
52 |
53 | if (!notification.sticky) {
54 | clearTimeout(this.notificationCloseTimeout);
55 | this.notificationCloseTimeout = setTimeout(this.hideNotification.bind(this), 15 * 1000, false);
56 | }
57 | }
58 | });
59 | }
60 |
61 | public ngOnDestroy(): void {
62 | this.subscriptions.unsubscribe();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/reset/reset.component.html:
--------------------------------------------------------------------------------
1 |
2 |
Are you sure you want to reset the configuration?
3 |
4 | no
5 | yes
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/components/reset/reset.component.scss:
--------------------------------------------------------------------------------
1 | .reset {
2 | &-container {
3 | position: absolute;
4 | z-index: 20;
5 | top: 10vh;
6 | left: 20vw;
7 | height: 56vh;
8 | width: 60vw;
9 | padding-top: 25vh;
10 | background-color: var(--background);
11 | border-radius: 2vw;
12 | text-align: center;
13 | }
14 |
15 | &-button {
16 | padding: 1.4vh 2vw;
17 | border-radius: 1vw;
18 | box-shadow: 0 10px 19px -8px rgb(0 0 0 / 75%);
19 | font-size: 0.7rem;
20 | outline: 0;
21 | border: 0;
22 | margin: 7vh 2vw;
23 |
24 | &__no {
25 | background-color: var(--background-3);
26 | opacity: 0.8;
27 | }
28 |
29 | &__yes {
30 | background-color: var(--success);
31 | }
32 |
33 | &__wrapper {
34 | display: block;
35 | margin: auto;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/reset/reset.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Output } from '@angular/core';
2 |
3 | import { AppService } from '../../services/app.service';
4 | import { ElectronService } from '../../services/electron.service';
5 | import { SystemService } from '../../services/system/system.service';
6 |
7 | @Component({
8 | selector: 'app-reset',
9 | templateUrl: './reset.component.html',
10 | styleUrls: ['./reset.component.scss'],
11 | standalone: false,
12 | })
13 | export class ResetComponent {
14 | @Output() closeFunction = new EventEmitter(true);
15 |
16 | constructor(
17 | public service: AppService,
18 | private systemService: SystemService,
19 | private electronService: ElectronService,
20 | ) {}
21 |
22 | public closeResetWindow(): void {
23 | this.closeFunction.emit();
24 | }
25 |
26 | public reset(): void {
27 | this.electronService.send('resetConfig');
28 | this.electronService.send('reload');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/settings/settings-icon/settings-icon.component.html:
--------------------------------------------------------------------------------
1 |
10 |
11 |
--------------------------------------------------------------------------------
/src/components/settings/settings-icon/settings-icon.component.scss:
--------------------------------------------------------------------------------
1 | .settings-icon {
2 | position: fixed;
3 | top: 5vh;
4 | right: 3vw;
5 | font-size: 6vw;
6 |
7 | &__update-notifier {
8 | position: absolute;
9 | width: 2.2vw;
10 | height: 2.2vw;
11 | border-radius: 100%;
12 | background-color: #e84118;
13 | bottom: 0;
14 | right: 0;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/settings/settings-icon/settings-icon.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | import { AppService } from '../../../services/app.service';
4 | import { ConfigService } from '../../../services/config.service';
5 |
6 | @Component({
7 | selector: 'app-settings-icon',
8 | templateUrl: './settings-icon.component.html',
9 | styleUrls: ['./settings-icon.component.scss'],
10 | standalone: false,
11 | })
12 | export class SettingsIconComponent {
13 | public constructor(
14 | public service: AppService,
15 | public configService: ConfigService,
16 | ) {}
17 |
18 | public settingsVisible = false;
19 |
20 | public showSettings(): void {
21 | this.settingsVisible = true;
22 | }
23 |
24 | public hideSettings(): void {
25 | setTimeout((): void => {
26 | this.settingsVisible = false;
27 | }, 350);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/setup/discover-octoprint/discover-octoprint.component.html:
--------------------------------------------------------------------------------
1 |
2 | First things first: Please select your OctoPrint instance from the list below or enter the IP/URL manually.
3 |
4 |
5 |
6 |
13 |
14 | {{ node.name }}
15 |
16 |
17 | Upgrade Required /
18 |
19 |
20 | Version {{ node.version }}, URL: {{ node.url }}
21 |
22 |
23 |
24 |
searching
25 |
26 |
enter manually
27 |
28 |
29 |
54 |
55 | search again
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/components/setup/discover-octoprint/discover-octoprint.component.scss:
--------------------------------------------------------------------------------
1 | .discover-octoprint {
2 | &__wrapper {
3 | height: 40vh;
4 | padding: 4vh 3vw;
5 | margin-right: 2vw;
6 | overflow: hidden auto;
7 | }
8 |
9 | &__searching {
10 | display: block;
11 | text-align: center;
12 | margin-top: 10vh;
13 | font-size: 0.8rem;
14 | }
15 |
16 | &__node {
17 | display: block;
18 | background-color: var(--background-3);
19 | border-radius: 1vw;
20 | margin-bottom: 2vh;
21 | padding: 2vh 2vw;
22 | background-image: url('../../../assets/connect.svg');
23 | background-repeat: no-repeat;
24 | background-position: right;
25 |
26 | &-disabled {
27 | pointer-events: none;
28 | opacity: 0.5;
29 | font-style: italic;
30 | }
31 |
32 | &-name {
33 | white-space: nowrap;
34 | text-overflow: ellipsis;
35 | font-size: 0.8rem;
36 | }
37 |
38 | &-details {
39 | font-size: 0.55rem;
40 | opacity: 0.7;
41 | }
42 |
43 | &-upgrade {
44 | font-size: 0.55rem;
45 | font-weight: bold;
46 | }
47 | }
48 |
49 | &__manual {
50 | text-align: center;
51 | font-size: 0.7rem;
52 | line-height: 14vh;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/setup/extruder-information/extruder-information.component.scss:
--------------------------------------------------------------------------------
1 | form {
2 | margin-left: 4vw;
3 | }
4 |
5 | .setup__explanation {
6 | margin-top: 4vh !important;
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/setup/extruder-information/extruder-information.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, Output } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-config-setup-extruder-information',
5 | templateUrl: './extruder-information.component.html',
6 | styleUrls: ['./extruder-information.component.scss', '../setup.component.scss'],
7 | standalone: false,
8 | })
9 | export class ExtruderInformationComponent {
10 | @Input() feedLength: number;
11 | @Input() feedSpeed: number;
12 |
13 | @Output() feedLengthChange = new EventEmitter();
14 | @Output() feedSpeedChange = new EventEmitter();
15 |
16 | changeFeedLength(amount: number): void {
17 | if (this.feedLength + amount < 0) {
18 | this.feedLength = 0;
19 | } else if (this.feedLength + amount > 9999) {
20 | this.feedLength = 9999;
21 | } else {
22 | this.feedLength += amount;
23 | }
24 | this.feedLengthChange.emit(this.feedLength);
25 | }
26 |
27 | changeFeedSpeed(amount: number): void {
28 | if (this.feedSpeed + amount < 0) {
29 | this.feedSpeed = 0;
30 | } else if (this.feedSpeed + amount > 999) {
31 | this.feedSpeed = 999;
32 | } else {
33 | this.feedSpeed += amount;
34 | }
35 | this.feedSpeedChange.emit(this.feedSpeed);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/setup/invalid-config/invalid-config.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/src/components/setup/invalid-config/invalid-config.component.scss:
--------------------------------------------------------------------------------
1 | .invalid-config {
2 | &__top-icon {
3 | width: 16vw;
4 | display: block;
5 | margin-left: calc(50% - 8vw);
6 | margin-top: -4vh;
7 | margin-bottom: -8vh;
8 | }
9 |
10 | &__header {
11 | display: block;
12 | text-align: center;
13 | font-size: 6vw;
14 | }
15 |
16 | &__sub-header {
17 | text-align: center;
18 | font-size: 3vw;
19 | display: block;
20 | }
21 |
22 | &__error-list {
23 | margin-left: 2vw;
24 | margin-top: 5vh;
25 | list-style-type: disc;
26 |
27 | & li {
28 | margin-top: 2vh;
29 | font-size: 2.8vw;
30 | font-family: Cousine, monospace;
31 |
32 | &::before {
33 | content: '-';
34 | margin-right: 2vw;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/setup/invalid-config/invalid-config.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | import { ConfigService } from '../../../services/config.service';
4 | import { ElectronService } from '../../../services/electron.service';
5 |
6 | @Component({
7 | selector: 'app-config-invalid',
8 | templateUrl: './invalid-config.component.html',
9 | styleUrls: ['./invalid-config.component.scss'],
10 | standalone: false,
11 | })
12 | export class ConfigInvalidComponent implements OnInit {
13 | public errors: string[];
14 |
15 | public constructor(
16 | private configService: ConfigService,
17 | private electronService: ElectronService,
18 | ) {
19 | this.electronService.send('resetConfig');
20 | }
21 |
22 | public ngOnInit(): void {
23 | this.errors = this.configService.getErrors();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/setup/octoprint-authentication/octoprint-authentication.component.html:
--------------------------------------------------------------------------------
1 |
2 | Please authenticate me now. You can either click the button below and confirm the request in your OctoPrint
3 | webinterface or enter an API Key manually.
4 |
5 |
6 | send request
7 |
8 |
20 |
--------------------------------------------------------------------------------
/src/components/setup/octoprint-authentication/octoprint-authentication.component.scss:
--------------------------------------------------------------------------------
1 | .octoprint-authentication {
2 | &__login-button {
3 | text-align: center;
4 | padding: 8vh 0 0;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/setup/octoprint-authentication/octoprint-authentication.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient, HttpResponseBase } from '@angular/common/http';
2 | import { Injectable } from '@angular/core';
3 | import { Observable } from 'rxjs';
4 | import { pluck } from 'rxjs/operators';
5 |
6 | import { AppToken } from '../../../model/octoprint';
7 |
8 | @Injectable({
9 | providedIn: 'root',
10 | })
11 | @Injectable()
12 | export class OctoprintAuthenticationService {
13 | constructor(private http: HttpClient) {}
14 |
15 | public probeAuthSupport(octoprintURL: string): Observable {
16 | return this.http.get(`${octoprintURL}plugin/appkeys/probe`, { observe: 'response' });
17 | }
18 |
19 | public startAuthProcess(octoprintURL: string): Observable {
20 | return this.http
21 | .post(`${octoprintURL}plugin/appkeys/request`, { app: 'OctoDash' })
22 | .pipe(pluck('app_token'));
23 | }
24 |
25 | public pollAuthProcessStatus(octoprintURL: string, token: string): Observable {
26 | return this.http.get(`${octoprintURL}plugin/appkeys/request/${token}`, { observe: 'response' });
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/setup/personalization/personalization.component.html:
--------------------------------------------------------------------------------
1 | Now tell me some facts about your setup so I can personalize OctoDash for you.
4 |
19 |
20 | You can change all settings (and even more) in the settings menu anytime. There is also a description of each
21 | attribute available in the GitHub Wiki.
22 |
23 |
--------------------------------------------------------------------------------
/src/components/setup/personalization/personalization.component.scss:
--------------------------------------------------------------------------------
1 | .personalization {
2 | &__input {
3 | margin: 5vh 0 3.5vh;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/setup/personalization/personalization.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
2 |
3 | import { PersonalizationService } from './personalization.service';
4 |
5 | @Component({
6 | selector: 'app-config-setup-personalization',
7 | templateUrl: './personalization.component.html',
8 | styleUrls: ['./personalization.component.scss', '../setup.component.scss'],
9 | standalone: false,
10 | })
11 | export class PersonalizationComponent implements OnInit {
12 | @Input() printerName: string;
13 | @Input() useTouchscreen: boolean;
14 | @Input() octoprintURL: string;
15 | @Input() apiKey: string;
16 |
17 | @Output() printerNameChange = new EventEmitter();
18 | @Output() useTouchscreenChange = new EventEmitter();
19 |
20 | constructor(private personalizationService: PersonalizationService) {}
21 |
22 | ngOnInit(): void {
23 | this.personalizationService
24 | .getActivePrinterProfileName(this.octoprintURL, this.apiKey)
25 | .subscribe((printerName: string) => {
26 | if (!this.printerName) {
27 | this.printerName = printerName;
28 | this.printerNameChange.emit(this.printerName);
29 | }
30 | });
31 | }
32 |
33 | changeUseTouchscreen(): void {
34 | this.useTouchscreen = !this.useTouchscreen;
35 | this.useTouchscreenChange.emit(this.useTouchscreen);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/setup/personalization/personalization.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient, HttpHeaders } from '@angular/common/http';
2 | import { Injectable } from '@angular/core';
3 | import { Observable } from 'rxjs';
4 | import { defaultIfEmpty, map } from 'rxjs/operators';
5 |
6 | import { OctoprintPrinterProfiles } from '../../../model/octoprint';
7 |
8 | @Injectable({
9 | providedIn: 'root',
10 | })
11 | export class PersonalizationService {
12 | public constructor(private http: HttpClient) {}
13 |
14 | public getActivePrinterProfileName(octoprintURL: string, apiKey: string): Observable {
15 | return this.http
16 | .get(`${octoprintURL}api/printerprofiles`, {
17 | headers: new HttpHeaders({
18 | 'x-api-key': apiKey,
19 | }),
20 | })
21 | .pipe(
22 | map(profiles => {
23 | for (const profile of Object.values(profiles.profiles)) {
24 | if (profile.current) return profile.name;
25 | }
26 | }),
27 | defaultIfEmpty(''),
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/setup/plugins/plugins.component.scss:
--------------------------------------------------------------------------------
1 | .setup__plugin-list {
2 | margin: 3vh 2.45vw 3vh 4vw;
3 | overflow-y: scroll;
4 | -webkit-overflow-scrolling: touch;
5 | height: 69vh;
6 |
7 | &::before {
8 | content: '';
9 | width: 91vw;
10 | height: 70vh;
11 | position: fixed;
12 | left: 2vw;
13 | top: 25vh;
14 | z-index: 2;
15 | pointer-events: none;
16 | background: linear-gradient(var(--background), transparent 6%, transparent 94%, var(--background));
17 | }
18 |
19 | span {
20 | display: block;
21 | font-size: 0.8rem;
22 | margin: 3vh 0;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/setup/plugins/plugins.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, Output } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-config-setup-plugins',
5 | templateUrl: './plugins.component.html',
6 | styleUrls: ['./plugins.component.scss', '../setup.component.scss'],
7 | standalone: false,
8 | })
9 | export class PluginsComponent {
10 | @Input() companionPlugin: boolean;
11 | @Input() displayLayerProgressPlugin: boolean;
12 | @Input() enclosurePlugin: boolean;
13 | @Input() filamentManagerPlugin: boolean;
14 | @Input() spoolManagerPlugin: boolean;
15 | @Input() preheatButtonPlugin: boolean;
16 | @Input() printTimeGeniusPlugin: boolean;
17 | @Input() psuControlPlugin: boolean;
18 | @Input() ophomPlugin: boolean;
19 | @Input() tpLinkSmartPlugPlugin: boolean;
20 | @Input() tuyaPlugin: boolean;
21 | @Input() tasmotaPlugin: boolean;
22 | @Input() tasmotaMqttPlugin: boolean;
23 | @Input() wemoPlugin: boolean;
24 |
25 | @Output() companionPluginChange = new EventEmitter();
26 | @Output() displayLayerProgressPluginChange = new EventEmitter();
27 | @Output() enclosurePluginChange = new EventEmitter();
28 | @Output() filamentManagerPluginChange = new EventEmitter();
29 | @Output() spoolManagerPluginChange = new EventEmitter();
30 | @Output() preheatButtonPluginChange = new EventEmitter();
31 | @Output() printTimeGeniusPluginChange = new EventEmitter();
32 | @Output() psuControlPluginChange = new EventEmitter();
33 | @Output() ophomPluginChange = new EventEmitter();
34 | @Output() tpLinkSmartPlugPluginChange = new EventEmitter();
35 | @Output() tuyaPluginChange = new EventEmitter();
36 | @Output() tasmotaPluginChange = new EventEmitter();
37 | @Output() tasmotaMqttPluginChange = new EventEmitter();
38 | @Output() wemoPluginChange = new EventEmitter();
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/setup/welcome/welcome.component.html:
--------------------------------------------------------------------------------
1 | Hey there!
2 |
3 | It looks like this is the first start of OctoDash.
4 |
5 | I'll help you set up your config and get you started.
6 |
7 |
8 | If you encounter any issues please check the troubleshooting guide in the GitHub Wiki.
9 |
10 |
11 |
12 | Note: you will need to connect a keyboard for the setup process
13 |
14 |
15 | Sorry for bothering you again ...
16 |
17 |
18 | We've released an update, so awesome, we needed to change the config. Please review your new config!
19 |
20 |
21 |
22 | Thanks for choosing OctoDash
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/components/setup/welcome/welcome.component.scss:
--------------------------------------------------------------------------------
1 | .welcome-screen {
2 | &__heading {
3 | display: block;
4 | text-align: center;
5 | font-size: 6vw;
6 | margin-top: 2vh;
7 | margin-bottom: 6vh;
8 | font-weight: 500;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/setup/welcome/welcome.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-config-setup-welcome',
5 | templateUrl: './welcome.component.html',
6 | styleUrls: ['./welcome.component.scss', '../setup.component.scss'],
7 | standalone: false,
8 | })
9 | export class WelcomeComponent {
10 | @Input() update: boolean;
11 | }
12 |
--------------------------------------------------------------------------------
/src/components/shared/hotend-icon/hotend-icon.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
-1">{{ tool }}
4 |
5 |
--------------------------------------------------------------------------------
/src/components/shared/hotend-icon/hotend-icon.component.scss:
--------------------------------------------------------------------------------
1 | .hotend-icon {
2 | position: relative;
3 |
4 | &__image {
5 | vertical-align: middle;
6 | display: inline-block;
7 | width: 6vw;
8 | }
9 |
10 | &__number {
11 | position: absolute;
12 | width: 6vw;
13 | text-align: center;
14 | top: 1.5vw;
15 | font-size: 1.5vw;
16 | text-shadow: 0 0 0.5vw red;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/shared/hotend-icon/hotend-icon.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-hotend-icon',
5 | templateUrl: './hotend-icon.component.html',
6 | styleUrls: ['./hotend-icon.component.scss'],
7 | standalone: false,
8 | })
9 | export class HotendIconComponent {
10 | @Input() tool: number;
11 | }
12 |
--------------------------------------------------------------------------------
/src/components/shared/quick-control/quick-control.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | +1
10 |
11 |
12 | +10
13 |
14 |
15 |
16 |
22 | {{ value }}{{ unit }}
23 |
24 |
25 |
26 |
27 | -1
28 |
29 |
30 | -10
31 |
32 |
33 |
34 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/components/shared/quick-control/quick-control.component.scss:
--------------------------------------------------------------------------------
1 | .quick-control {
2 | position: fixed;
3 | inset: 0;
4 | background-color: rgb(0 0 0 / 85%);
5 | transition: opacity 0.4s ease-in-out;
6 | z-index: 10;
7 |
8 | &__controller {
9 | width: 35vw;
10 | margin: 0 auto;
11 | border: solid 0.6vw;
12 | border-radius: 3vw;
13 | margin-top: 1.5vh;
14 |
15 | &-row {
16 | display: flex;
17 | }
18 |
19 | &-value {
20 | padding: 3vh 6vw;
21 | font-size: 8vw;
22 | font-weight: 500;
23 | text-align: center;
24 |
25 | &-unit {
26 | text-align: center;
27 | font-size: 4vw;
28 | font-weight: 400;
29 | }
30 | }
31 |
32 | &-increase {
33 | padding: 3vh 6vw;
34 | font-weight: 500;
35 | flex: 1;
36 | }
37 |
38 | &-decrease {
39 | padding: 3vh 6vw;
40 | font-weight: 500;
41 | flex: 1;
42 | }
43 |
44 | &-set {
45 | height: 10vw;
46 | border-top: solid 0.6vw;
47 | flex: 1;
48 | align-items: center;
49 | display: flex;
50 |
51 | &-span {
52 | text-align: center;
53 | flex: 1;
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/shared/quick-control/quick-control.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-quick-control',
5 | templateUrl: './quick-control.component.html',
6 | styleUrls: ['./quick-control.component.scss'],
7 | standalone: false,
8 | })
9 | export class QuickControlComponent implements OnInit {
10 | @Input() icon: string;
11 | @Input() unit: string;
12 | @Input() defaultValue: number;
13 |
14 | @Output() onBack = new EventEmitter();
15 | @Output() onSet = new EventEmitter();
16 |
17 | public value: number;
18 |
19 | public ngOnInit() {
20 | this.value = this.defaultValue;
21 | }
22 |
23 | public changeValue(value: number): void {
24 | this.value += value;
25 | if (this.value < -999) {
26 | this.value = this.defaultValue;
27 | } else if (this.value < 0) {
28 | this.value = 0;
29 | } else if (this.value > 999) {
30 | this.value = 999;
31 | }
32 | }
33 |
34 | public setValue(): void {
35 | this.onSet.emit(this.value);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/shared/toggle-switch/toggle-switch.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/components/shared/toggle-switch/toggle-switch.component.scss:
--------------------------------------------------------------------------------
1 | .wrapper[disabled='true'] {
2 | opacity: 0.5;
3 | pointer-events: none;
4 | }
5 |
6 | .checkbox__wrapper {
7 | height: 2rem;
8 | width: 3rem;
9 | display: inline-block;
10 | vertical-align: middle;
11 | margin: -0.5rem 0;
12 | }
13 |
14 | .label {
15 | display: inline-block;
16 | vertical-align: text-bottom;
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/shared/toggle-switch/toggle-switch.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, Output } from '@angular/core';
2 | import { AnimationItem } from 'lottie-web';
3 | import { AnimationOptions } from 'ngx-lottie';
4 |
5 | @Component({
6 | selector: 'app-toggle-switch',
7 | templateUrl: './toggle-switch.component.html',
8 | styleUrls: ['./toggle-switch.component.scss'],
9 | standalone: false,
10 | })
11 | export class ToggleSwitchComponent {
12 | @Input() value: boolean;
13 | @Input() disabled: boolean;
14 | @Output() valueChange = new EventEmitter();
15 |
16 | public toggleSwitchOptions: AnimationOptions = {
17 | path: 'assets/animations/toggle-switch.json',
18 | loop: false,
19 | };
20 |
21 | private animation: AnimationItem;
22 |
23 | public animationCreated(animation: AnimationItem): void {
24 | this.animation = animation;
25 | this.animation.autoplay = false;
26 | this.animation.setSpeed(4);
27 | if (this.value) {
28 | this.animation.goToAndStop(46, true);
29 | } else {
30 | this.animation.goToAndStop(0, true);
31 | }
32 | }
33 |
34 | public toggleValue(): void {
35 | if (this.disabled) {
36 | return;
37 | }
38 |
39 | this.value = !this.value;
40 | this.valueChange.emit(this.value);
41 | if (this.value) {
42 | this.animation.playSegments([1, 46], true);
43 | } else {
44 | this.animation.playSegments([46, 91], true);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/shared/top-bar/top-bar.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ back.text }}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | {{ next.text }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/components/shared/top-bar/top-bar.component.scss:
--------------------------------------------------------------------------------
1 | .top-bar {
2 | width: 100%;
3 | height: 10vh;
4 |
5 | & td {
6 | width: 33.3%;
7 | max-height: 10vh;
8 | }
9 |
10 | &__back {
11 | padding: 4vh 2vw 2vh;
12 | font-weight: 500;
13 |
14 | ::ng-deep &-icon {
15 | margin-right: 1.4vw;
16 | margin-left: 1vw;
17 | }
18 | }
19 |
20 | &__next {
21 | padding: 4vh 2vw 2vh;
22 | text-align: right;
23 | font-weight: 500;
24 |
25 | ::ng-deep &-icon {
26 | margin-left: 1.4vw;
27 | margin-right: 1vw;
28 | }
29 | }
30 |
31 | &__center {
32 | text-align: center;
33 |
34 | ::ng-deep &-icon {
35 | width: 7vw;
36 | max-height: 10vh;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/shared/top-bar/top-bar.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
2 | import { Router } from '@angular/router';
3 |
4 | type Button = {
5 | text?: string;
6 | icon?: string;
7 | };
8 |
9 | @Component({
10 | selector: 'app-top-bar',
11 | templateUrl: './top-bar.component.html',
12 | styleUrls: ['./top-bar.component.scss'],
13 | standalone: false,
14 | })
15 | export class TopBarComponent implements OnInit {
16 | @Input() backButton: Button | boolean;
17 | @Input() nextButton: Button | boolean;
18 |
19 | @Output() onBack = new EventEmitter();
20 | @Output() onNext = new EventEmitter();
21 |
22 | public back: Button;
23 | public next: Button;
24 |
25 | public constructor(private router: Router) {}
26 |
27 | ngOnInit(): void {
28 | if (this.backButton) {
29 | if (this.backButton === true) {
30 | this.backButton = {};
31 | }
32 | this.back = {
33 | text: this.backButton?.text || $localize`:@@ui-back:back`,
34 | icon: this.backButton?.icon || 'chevron-left',
35 | };
36 | }
37 | if (this.nextButton) {
38 | if (this.nextButton === true) {
39 | this.nextButton = {};
40 | }
41 | this.next = {
42 | text: this.nextButton?.text || $localize`:@@ui-next:next`,
43 | icon: this.nextButton?.icon || 'chevron-right',
44 | };
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/standby/standby.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Shhh! OctoDash is sleeping.
6 | Tap the screen to wake me up again.
7 |
8 |
9 | connecting
10 |
11 | Connection can't be established. Press screen to try again.
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/components/standby/standby.component.scss:
--------------------------------------------------------------------------------
1 | .standby {
2 | height: 100%;
3 |
4 | &__main-window {
5 | width: 100vw;
6 | margin-top: 15vh;
7 | display: inline-block;
8 | }
9 |
10 | &__custom-actions {
11 | display: inline-block;
12 | position: absolute;
13 | top: 20vh;
14 | }
15 |
16 | &__icon {
17 | height: 45vh;
18 | display: block;
19 | margin: auto;
20 | margin-bottom: 11vh;
21 | }
22 |
23 | &__text {
24 | &-big {
25 | display: block;
26 | text-align: center;
27 | margin-bottom: 5vh;
28 | font-size: 4vw;
29 | animation: fadeIn ease 0.6s;
30 | }
31 |
32 | &-small {
33 | display: block;
34 | text-align: center;
35 | margin-bottom: 5vh;
36 | font-size: 2vw;
37 | animation: fadeIn ease 0.6s;
38 | }
39 | }
40 |
41 | &__error {
42 | position: fixed;
43 | top: 20vh;
44 | left: 20vw;
45 | right: 20vw;
46 | text-align: center;
47 | background-color: black;
48 | opacity: 0.92;
49 | border-radius: 2vw;
50 | padding: 5vh 3vw;
51 | font-size: 3vw;
52 | z-index: 100;
53 |
54 | &-close {
55 | font-size: 2vw;
56 | opacity: 0.8;
57 | display: block;
58 | margin-top: 4vh;
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/standby/standby.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnDestroy, OnInit } from '@angular/core';
2 |
3 | import { PSUState } from '../../model';
4 | import { AppService } from '../../services/app.service';
5 | import { ConfigService } from '../../services/config.service';
6 | import { EnclosureService } from '../../services/enclosure/enclosure.service';
7 | import { SystemService } from '../../services/system/system.service';
8 |
9 | @Component({
10 | selector: 'app-standby',
11 | templateUrl: './standby.component.html',
12 | styleUrls: ['./standby.component.scss'],
13 | standalone: false,
14 | })
15 | export class StandbyComponent implements OnInit, OnDestroy {
16 | public connecting = false;
17 | public showConnectionError = false;
18 | private displaySleepTimeout: ReturnType;
19 | private connectErrorTimeout: ReturnType;
20 |
21 | public constructor(
22 | private configService: ConfigService,
23 | private service: AppService,
24 | private enclosureService: EnclosureService,
25 | private systemService: SystemService,
26 | ) {}
27 |
28 | public ngOnInit(): void {
29 | setTimeout(() => {
30 | if (this.configService.getAutomaticScreenSleep()) {
31 | this.displaySleepTimeout = setTimeout(this.service.turnDisplayOff.bind(this.service), 300000);
32 | }
33 | });
34 | }
35 |
36 | public ngOnDestroy(): void {
37 | clearTimeout(this.displaySleepTimeout);
38 | clearTimeout(this.connectErrorTimeout);
39 | if (this.configService.getAutomaticScreenSleep()) {
40 | this.service.turnDisplayOn();
41 | }
42 | }
43 |
44 | public reconnect(): void {
45 | this.connecting = true;
46 | if (this.configService.getAutomaticPrinterPowerOn()) {
47 | this.enclosureService.setPSUState(PSUState.ON);
48 | setTimeout(this.connectPrinter.bind(this), 5000);
49 | } else {
50 | this.connectPrinter();
51 | }
52 | }
53 |
54 | private connectPrinter(): void {
55 | this.systemService.connectPrinter();
56 | this.connectErrorTimeout = setTimeout(() => {
57 | this.showConnectionError = true;
58 | this.connectErrorTimeout = setTimeout(() => {
59 | this.showConnectionError = false;
60 | this.connecting = false;
61 | }, 30000);
62 | }, 15000);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/components/update/update.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
downloading update
4 |
5 |
9 | {{ updateProgress.speed }}
10 | MB/s
11 |
12 |
13 |
{{ updateProgress.total }}MB
14 |
{{ updateProgress.eta }} minutes left
17 |
18 |
19 |
installing update
20 |
23 |
this might take a while
24 |
25 |
26 |
v{{ service.latestVersion }} installed
27 |
would you like to restart OctoDash now?
28 |
29 | no
30 | yes
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/components/update/update.component.scss:
--------------------------------------------------------------------------------
1 | .update {
2 | &-container {
3 | position: absolute;
4 | z-index: 20;
5 | top: 10vh;
6 | left: 20vw;
7 | height: 81vh;
8 | width: 60vw;
9 | background-color: var(--background);
10 | border-radius: 2vw;
11 | }
12 |
13 | &-progress-bar {
14 | height: 5.5vh;
15 | border-radius: 2vh;
16 | background-color: var(--success);
17 | width: 25vw;
18 | transition: width 0.7s ease-in-out;
19 | font-size: 0.5rem;
20 | text-align: right;
21 | padding-top: 1.5vh;
22 | overflow: visible;
23 | white-space: nowrap;
24 |
25 | &-no-percentage {
26 | width: 10vw;
27 | animation: bounce-bar 2s ease-in-out infinite;
28 | margin-left: 0;
29 | }
30 |
31 | &__wrapper {
32 | width: 50vw;
33 | margin: 14vh auto 3vh;
34 | display: block;
35 | height: 7vh;
36 | background-color: transparent;
37 | border: 3px solid var(--border);
38 | border-radius: 3vh;
39 | }
40 | }
41 |
42 | &-heading {
43 | display: block;
44 | text-align: center;
45 | margin-top: 12vh;
46 | }
47 |
48 | &-size__total {
49 | display: block;
50 | text-align: right;
51 | font-size: 0.6rem;
52 | margin-right: 5vw;
53 | margin-top: -2vh;
54 | opacity: 0.6;
55 | }
56 |
57 | &-time__remaining {
58 | display: block;
59 | text-align: center;
60 | margin-top: 10vh;
61 | }
62 |
63 | &-notice {
64 | display: block;
65 | text-align: center;
66 | font-size: 0.5rem;
67 | margin-top: 20vh;
68 | }
69 |
70 | &-restart {
71 | display: block;
72 | text-align: center;
73 | font-size: 0.65rem;
74 | margin-top: 10vh;
75 | margin-bottom: 8vw;
76 |
77 | button {
78 | padding: 1.4vh 2vw;
79 | border-radius: 1vw;
80 | box-shadow: 0 10px 19px -8px rgb(0 0 0 / 75%);
81 | font-size: 0.7rem;
82 | outline: 0;
83 | border: 0;
84 | margin: 7vh 2vw;
85 |
86 | .button__no {
87 | background-color: var(--background-3);
88 | opacity: 0.8;
89 | }
90 |
91 | .button__yes {
92 | background-color: var(--success);
93 | }
94 |
95 | .button__wrapper {
96 | display: block;
97 | text-align: center;
98 | }
99 | }
100 | }
101 | }
102 |
103 | @keyframes bounce-bar {
104 | 0%,
105 | 100% {
106 | margin-left: 0;
107 | }
108 |
109 | 50% {
110 | margin-left: 40vw;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/components/update/update.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, NgZone, OnInit, Output } from '@angular/core';
2 |
3 | import { UpdateDownloadProgress, UpdateError } from '../../model';
4 | import { AppService } from '../../services/app.service';
5 | import { ElectronService } from '../../services/electron.service';
6 | import { NotificationService } from '../../services/notification.service';
7 | import { SystemService } from '../../services/system/system.service';
8 |
9 | @Component({
10 | selector: 'app-update',
11 | templateUrl: './update.component.html',
12 | styleUrls: ['./update.component.scss'],
13 | standalone: false,
14 | })
15 | export class UpdateComponent implements OnInit {
16 | @Output() closeFunction = new EventEmitter(true);
17 |
18 | public updateProgress: UpdateDownloadProgress = {
19 | percentage: 0,
20 | transferred: 0,
21 | total: '--.-',
22 | remaining: 0,
23 | eta: '--:--',
24 | runtime: '--:--',
25 | delta: 0,
26 | speed: '--.-',
27 | };
28 | public page = 1;
29 |
30 | constructor(
31 | public service: AppService,
32 | private notificationService: NotificationService,
33 | private systemService: SystemService,
34 | private zone: NgZone,
35 | private electronService: ElectronService,
36 | ) {}
37 |
38 | ngOnInit(): void {
39 | if (!this.service.getLatestVersion() || !this.service.getLatestVersionAssetsURL()) {
40 | this.notificationService.error(
41 | $localize`:@@error-update:Can't initiate update!`,
42 | $localize`:@@error-update-message:Some information is missing, please try again in an hour or update manually.`,
43 | );
44 | this.closeUpdateWindow();
45 | } else {
46 | this.setupListeners();
47 | this.update(this.service.getLatestVersionAssetsURL());
48 | }
49 | }
50 |
51 | private setupListeners(): void {
52 | this.electronService.on('updateError', (_, updateError: UpdateError): void => {
53 | this.notificationService.error(
54 | $localize`:@@error-install-update:Can't install update!`,
55 | updateError.error.message,
56 | );
57 | this.closeUpdateWindow();
58 | });
59 |
60 | this.electronService.on('updateDownloadProgress', (_, updateDownloadProgress: UpdateDownloadProgress): void => {
61 | this.zone.run(() => {
62 | this.updateProgress = updateDownloadProgress;
63 | });
64 | });
65 |
66 | this.electronService.on('updateDownloadFinished', (): void => {
67 | this.zone.run(() => {
68 | this.page = 2;
69 | });
70 | });
71 |
72 | this.electronService.on('updateInstalled', (): void => {
73 | this.zone.run(() => {
74 | this.page = 3;
75 | });
76 | });
77 | }
78 |
79 | public closeUpdateWindow(): void {
80 | this.page = 1;
81 | this.closeFunction.emit();
82 | }
83 |
84 | private update(assetsURL: string): void {
85 | this.electronService.send('update', {
86 | assetsURL: assetsURL,
87 | });
88 | }
89 |
90 | public restart(): void {
91 | this.electronService.send('restart');
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/directives/index.ts:
--------------------------------------------------------------------------------
1 | import { LongPress } from './long-press.directive';
2 |
3 | export default [LongPress];
4 |
--------------------------------------------------------------------------------
/src/directives/long-press.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core';
2 |
3 | @Directive({
4 | selector: '[long-press]',
5 | standalone: false,
6 | })
7 | export class LongPress {
8 | pressing: boolean;
9 | longPressing: boolean;
10 | timeout: ReturnType;
11 | interval: ReturnType;
12 |
13 | @Input() duration = 700;
14 | @Input() frequency = 100;
15 |
16 | @Output()
17 | onShortPress = new EventEmitter();
18 |
19 | @Output()
20 | onLongPress = new EventEmitter();
21 |
22 | @Output()
23 | onLongPressing = new EventEmitter();
24 |
25 | @HostListener('pointerdown', ['$event'])
26 | onMouseDown(event: PointerEvent): void {
27 | // Right clicks count as instant long presses
28 | const duration = event.button == 2 ? 0 : this.duration;
29 |
30 | this.pressing = true;
31 | this.longPressing = false;
32 |
33 | this.timeout = setTimeout(() => {
34 | this.longPressing = true;
35 | this.onLongPress.emit(event);
36 |
37 | this.interval = setInterval(() => {
38 | this.onLongPressing.emit(event);
39 | }, this.frequency);
40 | }, duration);
41 | }
42 |
43 | @HostListener('pointerup', ['$event'])
44 | endPress(event: PointerEvent): void {
45 | clearTimeout(this.timeout);
46 | clearInterval(this.interval);
47 |
48 | if (!this.longPressing && this.pressing) {
49 | this.onShortPress.emit(event);
50 | }
51 | this.longPressing = false;
52 | this.pressing = false;
53 | }
54 |
55 | @HostListener('pointerleave', ['$event'])
56 | endPressMove(): void {
57 | clearTimeout(this.timeout);
58 | clearInterval(this.interval);
59 |
60 | this.longPressing = false;
61 | this.pressing = false;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | };
4 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false,
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/src/favicon.ico
--------------------------------------------------------------------------------
/src/helper/config.js:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs';
2 |
3 | import { Ajv } from 'ajv';
4 | import Store from 'electron-store';
5 |
6 | import configSchema from './config.schema.json' with { type: 'json' };
7 |
8 | let store;
9 |
10 | const ajv = new Ajv({ useDefaults: true, allErrors: true });
11 |
12 | // Define keywords for schema->TS converter
13 | ajv.addKeyword('tsEnumNames');
14 | ajv.addKeyword('tsName');
15 | ajv.addKeyword('tsType');
16 |
17 | const validate = ajv.compile(configSchema);
18 |
19 | export function readConfig(window) {
20 | try {
21 | if (!store) {
22 | store = new Store();
23 | }
24 | const config = store.get('config');
25 | window.webContents.send('configRead', config);
26 | } catch {
27 | window.webContents.send('configError', "Can't read config file.");
28 | }
29 | }
30 | export function resetConfig(window) {
31 | try {
32 | store.delete('config');
33 | window.webContents.send('configErased');
34 | } catch {
35 | window.webContents.send('configError', "Can't reset config file.");
36 | }
37 | }
38 | export function saveConfig(window, config) {
39 | if (validate(config)) {
40 | try {
41 | store.set('config', config);
42 | window.webContents.send('configSaved', config);
43 | } catch {
44 | window.webContents.send('configError', "Can't save config file.");
45 | }
46 | } else {
47 | window.webContents.send('configSaveFail', getConfigErrors());
48 | }
49 | }
50 | export function checkConfig(window, config) {
51 | if (!validate(config)) {
52 | window.webContents.send('configFail', getConfigErrors());
53 | } else {
54 | window.webContents.send('configPass');
55 | }
56 | }
57 | function getConfigErrors() {
58 | const errors = [];
59 | validate.errors?.forEach(error => {
60 | if (error.keyword === 'type') {
61 | errors.push(`${error.instancePath} ${error.message}`);
62 | } else {
63 | errors.push(`${error.instancePath === '' ? '/' : error.instancePath} ${error.message}`);
64 | }
65 | });
66 | return errors;
67 | }
68 |
69 | function extractDefaults(data) {
70 | if ('default' in data) {
71 | return data.default;
72 | }
73 |
74 | if (data.type === 'object' && data.properties) {
75 | const obj = {};
76 | for (const [key, propdata] of Object.entries(data.properties)) {
77 | obj[key] = extractDefaults(propdata);
78 | }
79 | return obj;
80 | }
81 |
82 | if (data.type === 'array' && data.default) {
83 | return data.default;
84 | }
85 |
86 | return undefined;
87 | }
88 |
89 | export function writeDefaultConfig() {
90 | try {
91 | const defaultConfig = extractDefaults(configSchema);
92 | fs.writeFileSync(new URL('./config.default.json', import.meta.url), JSON.stringify(defaultConfig));
93 | } catch (e) {
94 | console.log(e);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/helper/discover.js:
--------------------------------------------------------------------------------
1 | import { exec } from 'node:child_process';
2 |
3 | import { Bonjour } from 'bonjour-service';
4 |
5 | const bonjour = new Bonjour();
6 |
7 | let browser;
8 | let nodes = [];
9 |
10 | export function startDiscovery(window) {
11 | exec('hostname', (err, stdout) => {
12 | if (err) {
13 | discoverNodes(window, null);
14 | } else {
15 | discoverNodes(window, `${stdout}.local`);
16 | }
17 | });
18 | }
19 |
20 | function discoverNodes(window, localDomain) {
21 | nodes = [];
22 | browser = bonjour.find({ type: 'octoprint' });
23 | browser.on('up', service => {
24 | nodes.push({
25 | id: service.addresses[0] + service.port,
26 | name: service.name,
27 | version: service.txt.version,
28 | url: `http://${service.host.replace(/\.$/, '')}:${service.port}${service.txt.path}`,
29 | local: service.host === localDomain,
30 | });
31 | sendNodes(window);
32 | });
33 |
34 | browser.on('down', service => {
35 | nodes = nodes.filter(node => node.id !== service.interfaceIndex);
36 | sendNodes(window);
37 | });
38 |
39 | browser.start();
40 | }
41 |
42 | export function stopDiscovery() {
43 | browser.stop();
44 | }
45 |
46 | function sendNodes(window) {
47 | window.webContents.send('discoveredNodes', nodes);
48 | }
49 |
--------------------------------------------------------------------------------
/src/helper/listener.js:
--------------------------------------------------------------------------------
1 | import { exec } from 'node:child_process';
2 |
3 | import { checkConfig, readConfig, resetConfig, saveConfig } from './config.js';
4 | import { startDiscovery, stopDiscovery } from './discover.js';
5 | import sendCustomStyles from './styles.js';
6 | import { downloadUpdate, restartOctoDash, sendVersionInfo } from './update.js';
7 |
8 | function activateScreenSleepListener(ipcMain) {
9 | ipcMain.on('screenControl', (_, screenCommand) => exec(screenCommand.command));
10 | }
11 |
12 | function activateReloadListener(ipcMain, window, url) {
13 | ipcMain.on('reload', () => {
14 | window.loadURL(url);
15 | });
16 | }
17 |
18 | function activateAppInfoListener(ipcMain, window, app) {
19 | ipcMain.on('appInfo', () => {
20 | sendCustomStyles(window);
21 | sendVersionInfo(window, app);
22 | });
23 | }
24 |
25 | function activateUpdateListener(ipcMain, window) {
26 | ipcMain.on('update', (_, updateInfo) => downloadUpdate(updateInfo, window));
27 | }
28 |
29 | function activateRestartListener(ipcMain, app) {
30 | ipcMain.on('restart', () => restartOctoDash(app));
31 | }
32 |
33 | function activateDiscoverListener(ipcMain, window) {
34 | ipcMain.on('discover', () => startDiscovery(window));
35 |
36 | ipcMain.on('stopDiscover', () => stopDiscovery());
37 | }
38 |
39 | function activateConfigListener(ipcMain, window) {
40 | ipcMain.on('readConfig', () => readConfig(window));
41 | ipcMain.on('resetConfig', () => resetConfig(window));
42 | ipcMain.on('saveConfig', (_, config) => saveConfig(window, config));
43 | ipcMain.on('checkConfig', (_, config) => checkConfig(window, config));
44 | }
45 |
46 | function activateListeners(ipcMain, window, app, url) {
47 | activateConfigListener(ipcMain, window);
48 | activateAppInfoListener(ipcMain, window, app);
49 | activateScreenSleepListener(ipcMain);
50 | activateReloadListener(ipcMain, window, url);
51 | activateRestartListener(ipcMain, app);
52 | activateUpdateListener(ipcMain, window);
53 | activateDiscoverListener(ipcMain, window);
54 | }
55 |
56 | export default activateListeners;
57 |
--------------------------------------------------------------------------------
/src/helper/protocol.js:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs';
2 | import path from 'node:path';
3 |
4 | import electron from 'electron';
5 | import mime from 'mime-types';
6 |
7 | const { app, protocol } = electron;
8 |
9 | function createProtocol(scheme, basePath) {
10 | if (!app.isReady()) return app.on('ready', () => createProtocol(...arguments));
11 |
12 | protocol.registerBufferProtocol(scheme, (request, callback) => {
13 | const filePath = path.join(basePath, request.url.replace(`${scheme}://`, ''));
14 | fs.readFile(filePath, (error, buffer) => {
15 | if (error) {
16 | fs.readFile(path.join(basePath, 'index.html'), (_, buffer) => {
17 | callback({ mimeType: mime.lookup('index.html'), data: buffer });
18 | });
19 | } else {
20 | callback({ mimeType: mime.lookup(filePath), data: buffer });
21 | }
22 | });
23 | });
24 | }
25 |
26 | export default createProtocol;
27 |
--------------------------------------------------------------------------------
/src/helper/styles.js:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs';
2 | import path from 'node:path';
3 |
4 | import electron from 'electron';
5 |
6 | const { app } = electron;
7 |
8 | function sendCustomStyles(window) {
9 | fs.readFile(path.join(app.getPath('userData'), 'custom-styles.css'), 'utf-8', (err, data) => {
10 | if (err) {
11 | if (err.code === 'ENOENT') {
12 | fs.writeFile(path.join(app.getPath('userData'), 'custom-styles.css'), '', err => {
13 | if (err) {
14 | window.webContents.send('customStylesError', err);
15 | } else {
16 | window.webContents.send('customStyles', '');
17 | }
18 | });
19 | } else {
20 | window.webContents.send('customStylesError', err);
21 | }
22 | } else {
23 | window.webContents.send('customStyles', data);
24 | }
25 | });
26 | }
27 |
28 | export default sendCustomStyles;
29 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | OctoDash
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | by UnchartedBull + contributors
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { enableProdMode } from '@angular/core';
4 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
5 |
6 | import { AppModule } from './app.module';
7 | import { environment } from './environments/environment';
8 |
9 | if (environment.production) {
10 | enableProdMode();
11 | }
12 |
13 | platformBrowserDynamic()
14 | .bootstrapModule(AppModule)
15 | .catch((err): void => console.error(err));
16 |
--------------------------------------------------------------------------------
/src/model/auth.model.ts:
--------------------------------------------------------------------------------
1 | export interface SocketAuth {
2 | user: string;
3 | session: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/model/enclosure.model.ts:
--------------------------------------------------------------------------------
1 | export interface TemperatureReading {
2 | temperature: number;
3 | humidity: number;
4 | unit: string;
5 | }
6 |
7 | export enum PSUState {
8 | ON,
9 | OFF,
10 | }
11 |
--------------------------------------------------------------------------------
/src/model/event.model.ts:
--------------------------------------------------------------------------------
1 | export enum PrinterEvent {
2 | PRINTING,
3 | PAUSED,
4 | CLOSED,
5 | CONNECTED,
6 | IDLE,
7 | UNKNOWN,
8 | }
9 |
10 | export interface PrinterNotification {
11 | message?: string;
12 | action?: string;
13 | text?: string;
14 | choices?: string[];
15 | }
16 |
--------------------------------------------------------------------------------
/src/model/filament.model.ts:
--------------------------------------------------------------------------------
1 | export interface FilamentSpoolList {
2 | spools: Array;
3 | }
4 |
5 | export interface FilamentSpool {
6 | id: number;
7 | name: string;
8 | displayName: string;
9 | color: string;
10 | material: string;
11 | temperatureOffset: number;
12 | used: number;
13 | weight: number;
14 | vendor: string;
15 | diameter: number;
16 | density: number;
17 | }
18 |
--------------------------------------------------------------------------------
/src/model/files.model.ts:
--------------------------------------------------------------------------------
1 | export interface Directory {
2 | folders: Array;
3 | files: Array;
4 | }
5 |
6 | export interface Folder {
7 | origin: string;
8 | path: string;
9 | name: string;
10 | size: string;
11 | }
12 |
13 | export interface File {
14 | origin: string;
15 | path: string;
16 | name: string;
17 | size: string;
18 | thumbnail: string;
19 | printTime?: string;
20 | filamentWeight?: number;
21 | date?: string;
22 | successful: 'files__object--success' | 'files__object--failed' | 'files__object--unknown';
23 | }
24 |
--------------------------------------------------------------------------------
/src/model/index.ts:
--------------------------------------------------------------------------------
1 | export * from './auth.model';
2 | export * from './config.model';
3 | export * from './enclosure.model';
4 | export * from './event.model';
5 | export * from './filament.model';
6 | export * from './files.model';
7 | export * from './job.model';
8 | export * from './printer-profile.model';
9 | export * from './printer.model';
10 | export * from './system.model';
11 | export * from './util-test.model';
12 |
--------------------------------------------------------------------------------
/src/model/job.model.ts:
--------------------------------------------------------------------------------
1 | export interface JobStatus {
2 | file: string;
3 | fullPath: string;
4 | progress: number;
5 | zHeight: number | ZHeightLayer;
6 | filamentAmount?: number;
7 | timePrinted: Duration;
8 | timeLeft?: Duration;
9 | estimatedPrintTime?: Duration;
10 | estimatedEndTime?: string;
11 | }
12 |
13 | export interface Duration {
14 | value: string;
15 | unit: string;
16 | }
17 |
18 | export interface ZHeightLayer {
19 | current: number;
20 | total: number;
21 | }
22 |
--------------------------------------------------------------------------------
/src/model/octoprint/auth.model.ts:
--------------------------------------------------------------------------------
1 | export interface OctoprintLogin {
2 | _is_external_client: boolean;
3 | _login_mechanism: string;
4 | active: boolean;
5 | admin: boolean;
6 | apikey: string;
7 | groups: Array;
8 | name: string;
9 | needs: {
10 | groups: Array;
11 | role: Array;
12 | };
13 | permissions: Array;
14 | roles: Array;
15 | session: string;
16 | user: boolean;
17 | }
18 |
19 | export interface AppToken {
20 | app_token: string;
21 | }
22 |
23 | export interface TokenSuccess {
24 | api_key: string;
25 | }
26 |
--------------------------------------------------------------------------------
/src/model/octoprint/connection.model.ts:
--------------------------------------------------------------------------------
1 | export interface ConnectCommand {
2 | command: string;
3 | port?: string;
4 | baudrate?: number;
5 | printerProfile?: string;
6 | save?: boolean;
7 | autoconnect?: boolean;
8 | }
9 |
--------------------------------------------------------------------------------
/src/model/octoprint/file.model.ts:
--------------------------------------------------------------------------------
1 | import { OctoprintFilament } from './socket.model';
2 |
3 | export interface OctoprintFile {
4 | date: number;
5 | display: string;
6 | gcodeAnalysis?: OctoprintGCodeAnalysis;
7 | hash: string;
8 | name: string;
9 | origin: string;
10 | path: string;
11 | prints: OctoprintPrints;
12 | refs: OctoprintRefs;
13 | size: number;
14 | statistics?: Record;
15 | type: string;
16 | typePath: [string];
17 | thumbnail?: string;
18 | }
19 |
20 | export interface OctoprintFolder {
21 | children: [OctoprintFile & OctoprintFolder];
22 | display: string;
23 | name: string;
24 | origin: string;
25 | path: string;
26 | refs: OctoprintRefs;
27 | type: string;
28 | typePath: [string];
29 | }
30 |
31 | interface OctoprintGCodeAnalysis {
32 | analysisFirstFilamentPrintTime: number;
33 | analysisLastFilamentPrintTime: number;
34 | analysisPending: boolean;
35 | analysisPrintTime: number;
36 | compensatedPrintTime: number;
37 | dimensions: {
38 | depth: number;
39 | height: number;
40 | width: number;
41 | };
42 | estimatedPrintTime: number;
43 | filament: OctoprintFilament;
44 | firstFilament: number;
45 | lastFilament: number;
46 | printingArea: {
47 | maxX: number;
48 | maxY: number;
49 | maxZ: number;
50 | minX: number;
51 | minY: number;
52 | minZ: number;
53 | };
54 | progress: [[number, number]];
55 | }
56 |
57 | interface OctoprintPrints {
58 | failure: number;
59 | success: number;
60 | last: {
61 | date: number;
62 | printTime: number;
63 | success: boolean;
64 | };
65 | }
66 |
67 | interface OctoprintRefs {
68 | download?: string;
69 | resource: string;
70 | }
71 |
72 | export interface FileCommand {
73 | command: 'select';
74 | print: boolean;
75 | }
76 |
--------------------------------------------------------------------------------
/src/model/octoprint/index.ts:
--------------------------------------------------------------------------------
1 | export * from './auth.model';
2 | export * from './connection.model';
3 | export * from './file.model';
4 | export * from './job.model';
5 | export * from './printer-commands.model';
6 | export * from './printer-profile.model';
7 | export * from './socket.model';
8 |
9 | export * from './plugins/companion.model';
10 | export * from './plugins/display-layer-progress.model';
11 | export * from './plugins/enclosure.model';
12 | export * from './plugins/filament-manager.model';
13 | export * from './plugins/spool-manager.model';
14 | export * from './plugins/psucontrol.model';
15 | export * from './plugins/ophomplugstatus.model';
16 | export * from './plugins/tp-link.model';
17 | export * from './plugins/tasmota.model';
18 | export * from './plugins/tasmota-mqtt.model';
19 | export * from './plugins/tuya.model';
20 | export * from './plugins/wemo.model';
21 |
--------------------------------------------------------------------------------
/src/model/octoprint/job.model.ts:
--------------------------------------------------------------------------------
1 | export interface JobCommand {
2 | command: string;
3 | action?: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/companion.model.ts:
--------------------------------------------------------------------------------
1 | export interface CompanionData {
2 | fanspeed: object;
3 | }
4 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/display-layer-progress.model.ts:
--------------------------------------------------------------------------------
1 | export interface DisplayLayerProgressData {
2 | averageLayerDuration: string;
3 | averageLayerDurationInSeconds: string;
4 | changeFilamentCount: number;
5 | changeFilamentTimeLeft: string;
6 | changeFilamentTimeLeftInSeconds: string;
7 | currentHeight: string;
8 | currentHeightFormatted: string;
9 | currentLayer: string;
10 | estimatedChangeFilamentTime: string;
11 | estimatedEndTime: string;
12 | fanspeed: string;
13 | feedrate: string;
14 | feedrateG0: string;
15 | feedrateG1: string;
16 | lastLayerDuration: string;
17 | m73progress: string;
18 | printTimeLeft: string;
19 | printTimeLeftInSeconds: string;
20 | printerState: string;
21 | progress: string;
22 | totalHeight: string;
23 | totalHeightFormatted: string;
24 | totalLayer: string;
25 | updateReason: string;
26 | }
27 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/enclosure.model.ts:
--------------------------------------------------------------------------------
1 | export interface EnclosurePluginAPI {
2 | controlled_io: string;
3 | temp_sensor_address: string;
4 | temp_sensor_navbar: boolean;
5 | temp_sensor_temp: number;
6 | printer_action: string;
7 | filament_sensor_enabled: boolean;
8 | controlled_io_set_value: number;
9 | temp_sensor_type: string;
10 | temp_sensor_humidity: number;
11 | filament_sensor_timeout: number;
12 | edge: string;
13 | ds18b20_serial: string;
14 | action_type: string;
15 | input_pull_resistor: string;
16 | input_type: string;
17 | label: string;
18 | index_id: number;
19 | use_fahrenheit: boolean;
20 | gpio_pin: string;
21 | }
22 |
23 | export interface EnclosureColorBody {
24 | red: number;
25 | green: number;
26 | blue: number;
27 | }
28 |
29 | export interface EnclosureOutputBody {
30 | status: boolean;
31 | }
32 |
33 | export interface EnclosurePWMBody {
34 | duty_cycle: number;
35 | }
36 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/filament-manager.model.ts:
--------------------------------------------------------------------------------
1 | export interface FilamentManagerSpoolList {
2 | spools: FilamentManagerSpool[];
3 | }
4 |
5 | export interface FilamentManagerSelections {
6 | selections: FilamentManagerSelection[];
7 | }
8 |
9 | export interface FilamentManagerSelectionPatch {
10 | selection: {
11 | tool: number;
12 | spool: {
13 | id: number;
14 | };
15 | };
16 | }
17 |
18 | interface FilamentManagerSelection {
19 | client_id: string;
20 | spool: FilamentManagerSpool;
21 | tool: number;
22 | }
23 |
24 | export interface FilamentManagerSpool {
25 | cost: number;
26 | id: number;
27 | name: string;
28 | displayName?: string;
29 | color?: string;
30 | profile: FilamentManagerProfile;
31 | temp_offset: number;
32 | used: number;
33 | weight: number;
34 | }
35 |
36 | interface FilamentManagerProfile {
37 | density: number;
38 | diameter: number;
39 | id: number;
40 | material: string;
41 | vendor: string;
42 | }
43 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/ophomplugstatus.model.ts:
--------------------------------------------------------------------------------
1 | export interface OphomPlugStatus {
2 | reponse: number;
3 | }
4 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/psucontrol.model.ts:
--------------------------------------------------------------------------------
1 | export interface PSUControlCommand {
2 | command: 'turnPSUOn' | 'turnPSUOff';
3 | }
4 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/spool-manager.model.ts:
--------------------------------------------------------------------------------
1 | export interface SpoolManagerSpoolList {
2 | allSpools: Array;
3 | selectedSpools: Array;
4 | catalogs: SpoolManagerCatalogs;
5 | templateSpool: SpoolManagerSpool;
6 | totalItemCount: string;
7 | }
8 |
9 | interface SpoolManagerCatalogs {
10 | labels: Array;
11 | materials: Array;
12 | vendors: Array;
13 | }
14 |
15 | export interface SpoolManagerSelectionPut {
16 | databaseId: number;
17 | toolIndex: number;
18 | }
19 |
20 | export interface SpoolManagerSpool {
21 | bedTemperature: number;
22 | code: unknown;
23 | color: string;
24 | colorName: string;
25 | cost: number;
26 | costUnit: string;
27 | created: string;
28 | databaseId: number;
29 | density: number;
30 | diameter: number;
31 | diameterTolerance: number;
32 | displayName: string;
33 | enclosureTemperature: number;
34 | firstUse: string;
35 | flowRateCompensation: number;
36 | isActive: boolean;
37 | isTemplate: boolean;
38 | labels: string;
39 | lastUse: string;
40 | material: string;
41 | materialCharacteristic: unknown;
42 | noteDeltaFormat: string;
43 | noteHtml: string;
44 | noteText: string;
45 | offsetBedTemperature: number;
46 | offsetEnclosureTemperature: number;
47 | offsetTemperature: number;
48 | originator: unknown;
49 | purchasedFrom: string;
50 | purchasedOn: string;
51 | remainingLength: string;
52 | remainingLengthPercentage: string;
53 | remainingPercentage: string;
54 | remainingWeight: string;
55 | spoolWeight: string;
56 | temperature: number;
57 | totalLength: number;
58 | totalWeight: number;
59 | updated: string;
60 | usedLength: number;
61 | usedLengthPercentage: string;
62 | usedPercentage: string;
63 | usedWeight: string;
64 | vendor: string;
65 | version: number;
66 | }
67 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/tasmota-mqtt.model.ts:
--------------------------------------------------------------------------------
1 | export interface TasmotaMqttCommand {
2 | command: 'turnOn' | 'turnOff';
3 | topic: string;
4 | relayN: number;
5 | }
6 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/tasmota.model.ts:
--------------------------------------------------------------------------------
1 | export interface TasmotaCommand {
2 | command: 'turnOn' | 'turnOff';
3 | ip: string;
4 | idx: number;
5 | }
6 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/tp-link.model.ts:
--------------------------------------------------------------------------------
1 | export interface TPLinkCommand {
2 | command: 'turnOn' | 'turnOff';
3 | ip: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/tuya.model.ts:
--------------------------------------------------------------------------------
1 | export interface TuyaCommand {
2 | command: 'turnOn' | 'turnOff';
3 | label: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/model/octoprint/plugins/wemo.model.ts:
--------------------------------------------------------------------------------
1 | export interface WemoCommand {
2 | command: 'turnOn' | 'turnOff';
3 | ip: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/model/octoprint/printer-commands.model.ts:
--------------------------------------------------------------------------------
1 | export interface JogCommand {
2 | command: 'jog';
3 | x: number;
4 | y: number;
5 | z: number;
6 | speed: number;
7 | }
8 |
9 | export interface ExtrudeCommand {
10 | command: 'extrude';
11 | amount: number;
12 | speed: number;
13 | }
14 |
15 | export interface GCodeCommand {
16 | commands: string[];
17 | }
18 |
19 | export interface FeedrateCommand {
20 | command: string;
21 | factor: number;
22 | }
23 |
24 | export interface ToolCommand {
25 | command: string;
26 | tool: string;
27 | }
28 |
29 | export interface TemperatureHotendCommand {
30 | command: string;
31 | targets: {
32 | [K in string as K extends string ? `tool${K}` : never]: number;
33 | };
34 | }
35 |
36 | export interface TemperatureHeatbedCommand {
37 | command: string;
38 | target: number;
39 | }
40 |
41 | export interface DisconnectCommand {
42 | command: string;
43 | }
44 |
--------------------------------------------------------------------------------
/src/model/octoprint/printer-profile.model.ts:
--------------------------------------------------------------------------------
1 | export interface OctoprintPrinterProfiles {
2 | profiles: {
3 | [key: string]: OctoprintPrinterProfile;
4 | };
5 | }
6 |
7 | export interface OctoprintPrinterProfile {
8 | current: boolean;
9 | name: string;
10 | model: string;
11 | axes: OctoprintPrinterAxis;
12 | extruder: OctoprintPrinterExtruders;
13 | }
14 |
15 | interface OctoprintPrinterAxis {
16 | x: OctoprintAxisDetails;
17 | y: OctoprintAxisDetails;
18 | z: OctoprintAxisDetails;
19 | }
20 |
21 | interface OctoprintAxisDetails {
22 | inverted: boolean;
23 | }
24 |
25 | interface OctoprintPrinterExtruders {
26 | count: number;
27 | offsets: OctoprintPrinterExtruderOffset[];
28 | sharedNozzle: boolean;
29 | }
30 |
31 | interface OctoprintPrinterExtruderOffset {
32 | x: number;
33 | y: number;
34 | }
35 |
--------------------------------------------------------------------------------
/src/model/octoprint/socket.model.ts:
--------------------------------------------------------------------------------
1 | import { OctoprintFile } from './file.model';
2 |
3 | export interface OctoprintSocketCurrent {
4 | current: {
5 | busyFiles: Array;
6 | currentZ: number;
7 | job: OctoprintJob;
8 | logs: Array;
9 | messages: Array;
10 | offsets: OctoprintOffsets;
11 | progress: OctoprintProgress;
12 | resends: OctoprintSocketResends;
13 | serverTime: number;
14 | state: OctoprintSocketState;
15 | temps: OctoprintSocketTemperatures;
16 | };
17 | }
18 |
19 | export interface OctoprintSocketEvent {
20 | event: {
21 | type: string;
22 | payload: {
23 | error: string;
24 | reason: string;
25 | };
26 | };
27 | }
28 | export interface OctoprintPluginMessage {
29 | plugin: {
30 | plugin: string;
31 | data: unknown;
32 | };
33 | }
34 |
35 | interface OctoprintJob {
36 | averagePrintTime: number;
37 | estimatedPrintTime: number;
38 | filament: OctoprintFilament;
39 | file: OctoprintFile;
40 | lastPrintTime: string;
41 | user: string;
42 | }
43 | export interface OctoprintFilament {
44 | [key: string]: OctoprintFilamentValues;
45 | }
46 |
47 | interface OctoprintFilamentValues {
48 | length: number;
49 | volume: number;
50 | }
51 |
52 | type OctoprintOffsets = {
53 | [K in string as K extends string ? `tool${K}` : never]: number;
54 | };
55 |
56 | interface OctoprintProgress {
57 | completion: number;
58 | filepos: number;
59 | printTime: number;
60 | printTimeLeft: number;
61 | printTimeLeftOrigin: string;
62 | }
63 |
64 | interface OctoprintSocketResends {
65 | count: number;
66 | transmitted: number;
67 | ratio: number;
68 | }
69 |
70 | interface OctoprintSocketState {
71 | text: string;
72 | flags: OctoprintSocketStateFlags;
73 | }
74 |
75 | interface OctoprintSocketStateFlags {
76 | cancelling: boolean;
77 | closedOrError: boolean;
78 | error: boolean;
79 | finishing: boolean;
80 | operational: boolean;
81 | paused: boolean;
82 | pausing: boolean;
83 | printing: boolean;
84 | ready: boolean;
85 | resuming: boolean;
86 | sdReady: boolean;
87 | }
88 |
89 | interface OctoprintSocketTemperatures {
90 | [key: number]: {
91 | time: number;
92 | bed: OctoprintSocketTemperature;
93 | chamber: OctoprintSocketTemperature;
94 | } & {
95 | [K in string as K extends string ? `tool${K}` : never]: OctoprintSocketTemperature;
96 | };
97 | }
98 |
99 | interface OctoprintSocketTemperature {
100 | actual: number;
101 | target: number;
102 | }
103 |
104 | export interface OctoprintVersionInfo {
105 | api: string;
106 | server: string;
107 | text: string;
108 | }
109 |
--------------------------------------------------------------------------------
/src/model/printer-profile.model.ts:
--------------------------------------------------------------------------------
1 | export interface PrinterProfile {
2 | current: boolean;
3 | name: string;
4 | model: string;
5 | axes: PrinterAxis;
6 | extruder: PrinterExtruders;
7 | }
8 |
9 | export interface PrinterAxis {
10 | x: AxisDetails;
11 | y: AxisDetails;
12 | z: AxisDetails;
13 | }
14 |
15 | export interface AxisDetails {
16 | inverted: boolean;
17 | }
18 |
19 | export interface PrinterExtruders {
20 | count: number;
21 | offsets: PrinterExtruderOffset[];
22 | sharedNozzle: boolean;
23 | }
24 |
25 | interface PrinterExtruderOffset {
26 | x: number;
27 | y: number;
28 | }
29 |
--------------------------------------------------------------------------------
/src/model/printer.model.ts:
--------------------------------------------------------------------------------
1 | export interface PrinterStatus {
2 | status: PrinterState;
3 | bed: Temperature;
4 | chamber: Temperature;
5 | fanSpeed: number;
6 | tools: Temperature[];
7 | }
8 |
9 | interface Temperature {
10 | current: number;
11 | set: number;
12 | unit: string;
13 | }
14 |
15 | export enum PrinterState {
16 | operational,
17 | pausing,
18 | paused,
19 | printing,
20 | cancelling,
21 | closed,
22 | connecting,
23 | reconnecting,
24 | socketDead,
25 | }
26 |
--------------------------------------------------------------------------------
/src/model/system.model.ts:
--------------------------------------------------------------------------------
1 | export interface Notification {
2 | heading: string;
3 | text: string;
4 | type: NotificationType;
5 | time?: Date;
6 | choices?: Array;
7 | callback?: (index: number) => void;
8 | sticky?: boolean;
9 | }
10 |
11 | export enum NotificationType {
12 | INFO,
13 | WARN,
14 | ERROR,
15 | PROMPT,
16 | }
17 |
18 | export interface UpdateError {
19 | error: {
20 | message: string;
21 | stack?: string;
22 | };
23 | }
24 |
25 | export interface UpdateDownloadProgress {
26 | percentage: number;
27 | transferred: number;
28 | total: number | string;
29 | remaining: number;
30 | eta: string;
31 | runtime: string;
32 | delta: number;
33 | speed: number | string;
34 | }
35 |
36 | export interface URLSplit {
37 | host: string;
38 | port: number;
39 | }
40 |
--------------------------------------------------------------------------------
/src/model/util-test.model.ts:
--------------------------------------------------------------------------------
1 | export interface TestAddress {
2 | address: string;
3 | is_lan_address: boolean;
4 | subnet: string;
5 | }
6 |
--------------------------------------------------------------------------------
/src/pipes/index.ts:
--------------------------------------------------------------------------------
1 | import { URLSafePipe } from './url.pipe';
2 |
3 | export default [URLSafePipe];
4 |
--------------------------------------------------------------------------------
/src/pipes/url.pipe.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 | import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
3 |
4 | @Pipe({
5 | name: 'url',
6 | standalone: false,
7 | })
8 | export class URLSafePipe implements PipeTransform {
9 | public constructor(private sanitizer: DomSanitizer) {}
10 |
11 | public transform(url: string): SafeResourceUrl {
12 | return this.sanitizer.bypassSecurityTrustResourceUrl(url);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/reset.css:
--------------------------------------------------------------------------------
1 | /* stylelint-disable */
2 |
3 | /* http://meyerweb.com/eric/tools/css/reset/
4 | v2.0 | 20110126
5 | License: none (public domain)
6 | */
7 |
8 | html,
9 | body,
10 | div,
11 | span,
12 | applet,
13 | object,
14 | iframe,
15 | h1,
16 | h2,
17 | h3,
18 | h4,
19 | h5,
20 | h6,
21 | p,
22 | blockquote,
23 | pre,
24 | a,
25 | abbr,
26 | acronym,
27 | address,
28 | big,
29 | cite,
30 | code,
31 | del,
32 | dfn,
33 | em,
34 | img,
35 | ins,
36 | kbd,
37 | q,
38 | s,
39 | samp,
40 | small,
41 | strike,
42 | strong,
43 | sub,
44 | sup,
45 | tt,
46 | var,
47 | b,
48 | u,
49 | i,
50 | center,
51 | dl,
52 | dt,
53 | dd,
54 | ol,
55 | ul,
56 | li,
57 | fieldset,
58 | form,
59 | label,
60 | legend,
61 | table,
62 | caption,
63 | tbody,
64 | tfoot,
65 | thead,
66 | tr,
67 | th,
68 | td,
69 | article,
70 | aside,
71 | canvas,
72 | details,
73 | embed,
74 | figure,
75 | figcaption,
76 | footer,
77 | header,
78 | hgroup,
79 | menu,
80 | nav,
81 | output,
82 | ruby,
83 | section,
84 | summary,
85 | time,
86 | mark,
87 | audio,
88 | video {
89 | margin: 0;
90 | padding: 0;
91 | border: 0;
92 | font: inherit;
93 | vertical-align: baseline;
94 | }
95 |
96 | /* HTML5 display-role reset for older browsers */
97 | article,
98 | aside,
99 | details,
100 | figcaption,
101 | figure,
102 | footer,
103 | header,
104 | hgroup,
105 | menu,
106 | nav,
107 | section {
108 | display: block;
109 | }
110 |
111 | body {
112 | line-height: 1.2;
113 | }
114 |
115 | ol,
116 | ul {
117 | list-style: none;
118 | }
119 |
120 | blockquote,
121 | q {
122 | quotes: none;
123 | }
124 |
125 | blockquote:before,
126 | blockquote:after,
127 | q:before,
128 | q:after {
129 | content: '';
130 | content: none;
131 | }
132 |
133 | table {
134 | border-collapse: collapse;
135 | border-spacing: 0;
136 | }
137 |
138 | input[type='number']::-webkit-inner-spin-button,
139 | input[type='number']::-webkit-outer-spin-button {
140 | -webkit-appearance: none;
141 | -moz-appearance: none;
142 | margin: 0;
143 | }
144 |
145 | select {
146 | -moz-appearance: none;
147 | -webkit-appearance: none;
148 | appearance: none;
149 | border: none;
150 | outline: none;
151 | }
152 |
153 | div:focus,
154 | a:focus,
155 | span:focus,
156 | td:focus,
157 | p:focus,
158 | img:focus {
159 | outline: 0;
160 | }
161 |
--------------------------------------------------------------------------------
/src/services/conversion.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | import { ConfigService } from './config.service';
4 |
5 | @Injectable()
6 | export class ConversionService {
7 | constructor(private configService: ConfigService) {}
8 |
9 | public convertByteToMegabyte(byte: number): string {
10 | return (byte / 1000000).toFixed(1);
11 | }
12 |
13 | public convertDateToString(date: Date): string {
14 | return `${('0' + date.getDate()).slice(-2)}.${('0' + (date.getMonth() + 1)).slice(-2)}.${date.getFullYear()} ${(
15 | '0' + date.getHours()
16 | ).slice(-2)}:${('0' + date.getMinutes()).slice(-2)}:${('0' + date.getSeconds()).slice(-2)}`;
17 | }
18 |
19 | public convertSecondsToHours(input: number): string {
20 | const hours = input / 60 / 60;
21 | let roundedHours = Math.floor(hours);
22 | const minutes = (hours - roundedHours) * 60;
23 | let roundedMinutes = Math.round(minutes);
24 | if (roundedMinutes === 60) {
25 | roundedMinutes = 0;
26 | roundedHours += 1;
27 | }
28 | return roundedHours + ':' + ('0' + roundedMinutes).slice(-2);
29 | }
30 |
31 | public convertFilamentLengthToWeight(filamentLength: number): number {
32 | return this.convertFilamentVolumeToWeight(
33 | (filamentLength * Math.PI * Math.pow(this.configService.getFilamentThickness() / 2, 2)) / 1000,
34 | );
35 | }
36 |
37 | private convertFilamentVolumeToWeight(filamentVolume: number): number {
38 | return Math.round(filamentVolume * this.configService.getFilamentDensity() * 10) / 10;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/services/electron.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { IpcRenderer } from 'electron';
3 |
4 | import { NotificationService } from './notification.service';
5 |
6 | @Injectable({
7 | providedIn: 'root',
8 | })
9 | export class ElectronService {
10 | private ipcRenderer: IpcRenderer | undefined;
11 |
12 | constructor(private notificationService: NotificationService) {
13 | if (window.require) {
14 | this.ipcRenderer = window.require('electron').ipcRenderer;
15 | } else {
16 | this.notificationService.error(
17 | "Can't load electron library",
18 | 'Please restart your system and open a new issue on GitHub if this issue persists.',
19 | true,
20 | );
21 | }
22 | }
23 |
24 | public on(channel: string, listener: (...args) => void): void {
25 | if (!this.ipcRenderer) {
26 | return;
27 | }
28 | this.ipcRenderer.on(channel, listener);
29 | }
30 |
31 | public removeListener(channel: string, listener: (...args) => void): void {
32 | if (!this.ipcRenderer) {
33 | return;
34 | }
35 | this.ipcRenderer.removeListener(channel, listener);
36 | }
37 |
38 | public send(channel: string, ...args): void {
39 | if (!this.ipcRenderer) {
40 | return;
41 | }
42 | this.ipcRenderer.send(channel, ...args);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/services/enclosure/enclosure.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable } from 'rxjs';
3 |
4 | import { PSUState, TemperatureReading } from '../../model';
5 |
6 | @Injectable()
7 | export abstract class EnclosureService {
8 | abstract getEnclosureTemperature(): Observable;
9 |
10 | abstract setLEDColor(identifier: number, red: number, green: number, blue: number): void;
11 |
12 | abstract setOutput(identifier: number, status: boolean): void;
13 |
14 | abstract setOutputPWM(identifier: number, dutyCycle: number): void;
15 |
16 | abstract runEnclosureShell(identifier: number): void;
17 |
18 | abstract setPSUState(state: PSUState): void;
19 |
20 | abstract togglePSU(): void;
21 | }
22 |
--------------------------------------------------------------------------------
/src/services/event.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, OnDestroy } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { Subscription } from 'rxjs';
4 |
5 | import { PrinterEvent } from '../model/event.model';
6 | import { ConfigService } from './config.service';
7 | import { SocketService } from './socket/socket.service';
8 |
9 | @Injectable()
10 | export class EventService implements OnDestroy {
11 | private subscriptions: Subscription = new Subscription();
12 |
13 | private printing = false;
14 |
15 | public constructor(
16 | private socketService: SocketService,
17 | private configService: ConfigService,
18 | private router: Router,
19 | ) {
20 | this.subscriptions.add(
21 | this.socketService.getEventSubscribable().subscribe((event: PrinterEvent) => this.handlePrinterEvent(event)),
22 | );
23 | }
24 |
25 | private handlePrinterEvent(event: PrinterEvent): void {
26 | if (event === PrinterEvent.PRINTING || event === PrinterEvent.PAUSED) {
27 | setTimeout(() => {
28 | this.printing = true;
29 | }, 500);
30 | } else {
31 | setTimeout(() => {
32 | this.printing = false;
33 | }, 1000);
34 | }
35 |
36 | if (event === PrinterEvent.CLOSED) {
37 | this.router.navigate(['/standby']);
38 | } else if (event === PrinterEvent.CONNECTED) {
39 | setTimeout(() => {
40 | this.router.navigate(['/main-screen']);
41 | }, 500);
42 | }
43 | }
44 |
45 | ngOnDestroy(): void {
46 | this.subscriptions.unsubscribe();
47 | }
48 |
49 | public isPrinting(): boolean {
50 | return this.printing;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/services/filament/filament-plugin.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable } from 'rxjs';
3 |
4 | import { FilamentSpool } from '../../model';
5 |
6 | @Injectable()
7 | export abstract class FilamentPluginService {
8 | abstract getSpools(): Observable>;
9 |
10 | abstract getCurrentSpools(): Observable;
11 |
12 | abstract getCurrentSpool(tool: number): Observable;
13 |
14 | abstract setSpool(spool: FilamentSpool, tool: number): Observable;
15 | }
16 |
--------------------------------------------------------------------------------
/src/services/files/files.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable } from 'rxjs';
3 |
4 | import { Directory, File } from '../../model';
5 |
6 | @Injectable()
7 | export abstract class FilesService {
8 | abstract getFolderContent(folderPath: string): Observable;
9 |
10 | abstract getFile(filePath: string): Observable;
11 |
12 | abstract getThumbnail(filePath: string): Observable;
13 |
14 | abstract loadFile(filePath: string): void;
15 |
16 | abstract printFile(filePath: string): void;
17 |
18 | abstract deleteFile(filePath: string): void;
19 |
20 | abstract setLoadedFile(value: boolean): void;
21 |
22 | abstract getLoadedFile(): boolean;
23 | }
24 |
--------------------------------------------------------------------------------
/src/services/job/job.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | @Injectable()
4 | export abstract class JobService {
5 | abstract startJob(): void;
6 |
7 | abstract pauseJob(): void;
8 |
9 | abstract resumeJob(): void;
10 |
11 | abstract cancelJob(): void;
12 |
13 | abstract restartJob(): void;
14 |
15 | abstract preheat(): void;
16 | }
17 |
--------------------------------------------------------------------------------
/src/services/notification.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable, Observer } from 'rxjs';
3 | import { shareReplay } from 'rxjs/operators';
4 |
5 | import { Notification, NotificationType } from '../model';
6 |
7 | @Injectable({
8 | providedIn: 'root',
9 | })
10 | export class NotificationService {
11 | private observable: Observable;
12 | private observer: Observer;
13 | private bootGrace = false;
14 |
15 | public notificationStack: Array = [];
16 |
17 | public constructor() {
18 | this.observable = new Observable((observer: Observer): void => {
19 | this.observer = observer;
20 | setTimeout((): void => {
21 | this.bootGrace = false;
22 | }, 30000);
23 | }).pipe(shareReplay({ bufferSize: 1, refCount: true }));
24 | }
25 |
26 | public info(heading: string, text: string, sticky?: boolean): void {
27 | this.setNotification({
28 | heading,
29 | text,
30 | type: NotificationType.INFO,
31 | sticky,
32 | });
33 | }
34 |
35 | public warn(heading: string, text: string, sticky?: boolean): void {
36 | this.setNotification({
37 | heading,
38 | text,
39 | type: NotificationType.WARN,
40 | sticky,
41 | });
42 | }
43 |
44 | public error(heading: string, text: string, sticky?: boolean): void {
45 | this.setNotification({
46 | heading,
47 | text,
48 | type: NotificationType.ERROR,
49 | sticky,
50 | });
51 | }
52 |
53 | public prompt(heading: string, text: string, choices: Array, callback: (index: number) => void): void {
54 | this.setNotification({
55 | heading,
56 | text,
57 | type: NotificationType.PROMPT,
58 | choices,
59 | callback,
60 | sticky: true,
61 | });
62 | }
63 |
64 | private setNotification(notification: Notification): void {
65 | if (!notification.time) {
66 | notification.time = new Date();
67 | }
68 | if (this.observer) {
69 | this.observer.next(notification);
70 | this.notificationStack.push(notification);
71 |
72 | if (this.notificationStack.length > 25) {
73 | this.notificationStack.shift();
74 | }
75 | } else {
76 | setTimeout(this.setNotification.bind(this), 1000, notification);
77 | }
78 | }
79 |
80 | public removeNotification(notification: Notification) {
81 | this.notificationStack = this.notificationStack.filter(n => n.time !== notification.time);
82 | }
83 |
84 | public closeNotification(): void {
85 | this.observer.next('close');
86 | }
87 |
88 | public getObservable(): Observable {
89 | return this.observable;
90 | }
91 |
92 | public getBootGrace(): boolean {
93 | return this.bootGrace;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/services/printer/printer.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable } from 'rxjs';
3 |
4 | import { PrinterProfile } from '../../model';
5 |
6 | @Injectable()
7 | export abstract class PrinterService {
8 | abstract getActiveProfile(): Observable;
9 |
10 | abstract saveToEPROM(): void;
11 |
12 | abstract executeGCode(gCode: string): void;
13 |
14 | abstract jog(x: number, y: number, z: number): void;
15 |
16 | abstract extrude(amount: number, speed: number, tool?: number): void;
17 |
18 | abstract setTool(tool: number): void;
19 |
20 | abstract setTemperatureHotend(temperature: number, tool?: number): void;
21 |
22 | abstract setTemperatureBed(temperature: number): void;
23 |
24 | abstract setFanSpeed(percentage: number): void;
25 |
26 | abstract setFeedrate(feedrate: number): void;
27 |
28 | abstract setFlowrate(flowrate: number): void;
29 |
30 | abstract disconnectPrinter(): void;
31 |
32 | abstract emergencyStop(): void;
33 | }
34 |
--------------------------------------------------------------------------------
/src/services/socket/socket.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable } from 'rxjs';
3 |
4 | import { JobStatus, PrinterEvent, PrinterNotification, PrinterStatus } from '../../model';
5 |
6 | @Injectable()
7 | export abstract class SocketService {
8 | abstract connect(): Promise;
9 |
10 | abstract getPrinterStatusSubscribable(): Observable;
11 |
12 | abstract getPrinterStatusText(): Observable;
13 |
14 | abstract getJobStatusSubscribable(): Observable;
15 |
16 | abstract getEventSubscribable(): Observable;
17 | }
18 |
--------------------------------------------------------------------------------
/src/services/system/system.octoprint.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { Injectable } from '@angular/core';
3 | import { Observable, of } from 'rxjs';
4 | import { catchError, map } from 'rxjs/operators';
5 |
6 | import { SocketAuth, TestAddress } from '../../model';
7 | import { ConnectCommand, OctoprintLogin } from '../../model/octoprint';
8 | import { ConfigService } from '../../services/config.service';
9 | import { NotificationService } from '../../services/notification.service';
10 | import { SystemService } from './system.service';
11 |
12 | @Injectable()
13 | export class SystemOctoprintService implements SystemService {
14 | constructor(
15 | private configService: ConfigService,
16 | private notificationService: NotificationService,
17 | private http: HttpClient,
18 | ) {}
19 |
20 | public getSessionKey(): Observable {
21 | return this.http
22 | .post(
23 | this.configService.getApiURL('login'),
24 | { passive: true },
25 | this.configService.getHTTPHeaders(),
26 | )
27 | .pipe(
28 | map(octoprintLogin => {
29 | return {
30 | user: octoprintLogin.name,
31 | session: octoprintLogin.session,
32 | } as SocketAuth;
33 | }),
34 | );
35 | }
36 |
37 | public sendCommand(command: string): void {
38 | this.http
39 | .post(this.configService.getApiURL(`system/commands/core/${command}`), null, this.configService.getHTTPHeaders())
40 | .pipe(
41 | catchError(error => {
42 | this.notificationService.error($localize`:@@error-execute:Can't execute ${command} command!`, error.message);
43 | return of(error);
44 | }),
45 | )
46 | .subscribe();
47 | }
48 |
49 | public connectPrinter(): void {
50 | const payload: ConnectCommand = {
51 | command: 'connect',
52 | save: false,
53 | };
54 |
55 | this.http
56 | .post(this.configService.getApiURL('connection'), payload, this.configService.getHTTPHeaders())
57 | .pipe(
58 | catchError(error => {
59 | this.notificationService.warn($localize`:@@error-connect:Can't connect to printer!`, error.message, true);
60 | return of(error);
61 | }),
62 | )
63 | .subscribe();
64 | }
65 |
66 | public getLocalIpAddress() {
67 | const payload = {
68 | command: 'address',
69 | };
70 |
71 | return this.http
72 | .post(this.configService.getApiURL('util/test'), payload, this.configService.getHTTPHeaders())
73 | .pipe(map(result => (result?.is_lan_address && result?.address ? result.address : null)));
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/services/system/system.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable } from 'rxjs';
3 |
4 | import { SocketAuth } from '../../model';
5 |
6 | @Injectable()
7 | export abstract class SystemService {
8 | abstract getLocalIpAddress(): Observable;
9 |
10 | abstract getSessionKey(): Observable;
11 |
12 | abstract sendCommand(command: string): void;
13 |
14 | abstract connectPrinter(): void;
15 | }
16 |
--------------------------------------------------------------------------------
/themes/NOX/README.md:
--------------------------------------------------------------------------------
1 | # NOX theme V0.2.2
2 |
3 | I suggest setting the thumbnail resolution in PrusaSlicer a little bit higher if you want a sharper image:
4 |
5 | ```
6 | thumbnails = 16x16,330x186
7 | ```
8 |
9 | You also need to activate this setting in OctoDash:
10 |
11 | **Settings > OctoDash > Preview by default while printing**
12 |
13 | If you want to use the round progress bar:
14 |
15 | **Settings > OctoDash > Always use circular progress bar**
16 |
17 | ### Screenshots:
18 |
19 | 1a. Printing Screen (progress bar):
20 |
21 | 
22 |
23 | 1b. Printing Screen (round progress bar):
24 |
25 | 
26 |
27 | 2. Main Screen:
28 |
29 | 
30 |
31 | 3. File List:
32 |
33 | 
34 |
35 | 4. File Details:
36 |
37 | 
38 |
39 | 5. File loaded:
40 |
41 | 
42 |
--------------------------------------------------------------------------------
/themes/NOX/screenshots/screenshot_file-loaded.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/NOX/screenshots/screenshot_file-loaded.png
--------------------------------------------------------------------------------
/themes/NOX/screenshots/screenshot_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/NOX/screenshots/screenshot_file.png
--------------------------------------------------------------------------------
/themes/NOX/screenshots/screenshot_files.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/NOX/screenshots/screenshot_files.png
--------------------------------------------------------------------------------
/themes/NOX/screenshots/screenshot_main-screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/NOX/screenshots/screenshot_main-screen.png
--------------------------------------------------------------------------------
/themes/NOX/screenshots/screenshot_printing1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/NOX/screenshots/screenshot_printing1.png
--------------------------------------------------------------------------------
/themes/NOX/screenshots/screenshot_printing2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/NOX/screenshots/screenshot_printing2.png
--------------------------------------------------------------------------------
/themes/dark-nights/README.md:
--------------------------------------------------------------------------------
1 | # Dark Nights
2 |
3 | A dark theme with customizeable accent color which matches my octoprint theme.
4 |
5 | ## Customize
6 |
7 | To customize the accent color edit the hex-codes at the beginning of the `custom-styles.css`.
8 |
9 | ```css
10 | :root {
11 | /* If a other accent color is more to your personal liking, change it here. ;) */
12 | --accent: #d32f2f;
13 | --accent-transparent: #d32f2f24;
14 | ...
15 | }
16 | ```
17 |
18 | Make sure to change also the transparent accent color.
19 |
20 | ## Screenshots
21 |
22 | ### Dashboard
23 |
24 | 
25 |
26 | 
27 |
28 | ### Files
29 |
30 | 
31 |
32 | 
33 |
34 | 
35 |
36 | ### Filament change
37 |
38 | 
39 |
40 | 
41 |
42 | ### Printing
43 |
44 | 
45 |
46 | 
47 |
48 | 
49 |
--------------------------------------------------------------------------------
/themes/dark-nights/screenshots/dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/dark-nights/screenshots/dashboard.png
--------------------------------------------------------------------------------
/themes/dark-nights/screenshots/filament-purge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/dark-nights/screenshots/filament-purge.png
--------------------------------------------------------------------------------
/themes/dark-nights/screenshots/filament-temp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/dark-nights/screenshots/filament-temp.png
--------------------------------------------------------------------------------
/themes/dark-nights/screenshots/file-browser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/dark-nights/screenshots/file-browser.png
--------------------------------------------------------------------------------
/themes/dark-nights/screenshots/file-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/dark-nights/screenshots/file-preview.png
--------------------------------------------------------------------------------
/themes/dark-nights/screenshots/file-selection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/dark-nights/screenshots/file-selection.png
--------------------------------------------------------------------------------
/themes/dark-nights/screenshots/printing-adjust.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/dark-nights/screenshots/printing-adjust.png
--------------------------------------------------------------------------------
/themes/dark-nights/screenshots/printing-percent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/dark-nights/screenshots/printing-percent.png
--------------------------------------------------------------------------------
/themes/dark-nights/screenshots/printing-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/dark-nights/screenshots/printing-preview.png
--------------------------------------------------------------------------------
/themes/dark-nights/screenshots/temp-adjust.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/dark-nights/screenshots/temp-adjust.png
--------------------------------------------------------------------------------
/themes/square/README.md:
--------------------------------------------------------------------------------
1 | # sqaure theme V1.0
2 |
3 | Theme for displays with square aspect ratio, like the HyperPixel Square. Courtesy of Kevin from the Discord server.
4 |
5 | ### Screenshots:
6 |
7 | will be added later
8 |
--------------------------------------------------------------------------------
/themes/theGarbz/BigFingers/README.md:
--------------------------------------------------------------------------------
1 | # BigFingers theme v0.3
2 |
3 | This variation on the standard theme increases the width of the scroll bars to make them easier to touch on small displays.
4 | No special requirements exist for this theme, however it is intended for small low resolution displays.
5 |
6 | To install copy the custom-styles.css file into the octodash config folder:
7 |
8 | ```
9 | ~/.config/octodash/custom-styles.css
10 | ```
11 |
12 | ###### Theme by theGarbz.
13 |
14 | ### Screenshots:
15 |
16 | 1. File List:
17 |
18 | 
19 |
20 | 2. Filament List:
21 |
22 | 
23 |
24 | 3. Settings:
25 |
26 | 
27 |
28 | ### Version History:
29 |
30 | **v0.3:**
31 |
32 | - Extra text removed from settings menu which ran off the screen.
33 |
34 | **v0.2:**
35 |
36 | - Hitbox of settings button increased.
37 |
38 | **v0.1:**
39 |
40 | - Initial Issue
41 | - Larger vertical scrollbar width.
42 |
--------------------------------------------------------------------------------
/themes/theGarbz/BigFingers/custom-styles.css:
--------------------------------------------------------------------------------
1 | /**** Override scrollbars ****/
2 |
3 | ::-webkit-scrollbar {
4 | width: 6vw !important;
5 | }
6 |
7 | /**** Resize file list ****/
8 |
9 | .files {
10 | height: 73vh !important;
11 | margin-top: 2vh !important;
12 | }
13 |
14 | .files__object {
15 | width: 86.5vw !important;
16 | height: 13vh !important;
17 | line-height: 12vh !important;
18 | }
19 |
20 | .files__info {
21 | height: 12vh !important;
22 | padding: 0.8vw 0 !important;
23 | }
24 |
25 | .files__icon {
26 | margin: 2.4vh 1vw 0 !important;
27 | }
28 |
29 | .files__name {
30 | height: 13vh;
31 | line-height: 13vh;
32 | }
33 |
34 | /**** Resize filament list ****/
35 |
36 | .filament tr {
37 | width: 86vw !important;
38 | height: 13vh !important;
39 | line-height: 13vh !important;
40 | }
41 |
42 | .filament-filaments {
43 | line-height: 13vh !important;
44 | margin-top: 4vh !important;
45 | }
46 |
47 | .filament-filaments td {
48 | line-height: 12vh !important;
49 | }
50 |
51 | .filament-filaments__name {
52 | width: 58vw !important;
53 | }
54 |
55 | /**** Make settings button easier to hit ****/
56 |
57 | .main-menu__settings-icon {
58 | padding: 5vh 5vw 4vh !important;
59 | }
60 |
61 | /**** Update Settings ****/
62 |
63 | .settings-container {
64 | height: 86vh !important;
65 | top: 8vh !important;
66 | width: 66vw !important;
67 | left: 17vw !important;
68 | }
69 |
70 | .settings__content {
71 | width: 59vw !important;
72 | }
73 |
--------------------------------------------------------------------------------
/themes/theGarbz/BigFingers/screenshots/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/BigFingers/screenshots/icon.png
--------------------------------------------------------------------------------
/themes/theGarbz/BigFingers/screenshots/screenshot_filaments.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/BigFingers/screenshots/screenshot_filaments.png
--------------------------------------------------------------------------------
/themes/theGarbz/BigFingers/screenshots/screenshot_files.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/BigFingers/screenshots/screenshot_files.png
--------------------------------------------------------------------------------
/themes/theGarbz/BigFingers/screenshots/screenshot_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/BigFingers/screenshots/screenshot_settings.png
--------------------------------------------------------------------------------
/themes/theGarbz/Focus/README.md:
--------------------------------------------------------------------------------
1 | # Focus theme v0.4
2 |
3 | This theme is based on the classy [NOX theme](../../NOX/) by NoxHirsch and has been modified to Focus on highlighting the most important information for a user. The goal was to create a theme that could easily be read from a distance. Some of the features are listed below:
4 |
5 | - High contrast display of relevant information (e.g. print time, temperature)
6 | - Low contrast display of all other info (e.g. units, time elapsed)
7 | - Large horizontal and vertical progress bars readable from a distance.
8 | - Large print time remaining indicators
9 | - Pre-printing screen highlighting buttons.
10 | - Full screen menus and adjustments with coloured backgrounds for context.
11 | - Red highlighting of error messages
12 |
13 | To install copy the custom-styles.css file into the octodash config folder:
14 |
15 | ```
16 | ~/.config/octodash/custom-styles.css
17 | ```
18 |
19 | Please note: This theme like the theme it was based on makes use of more CSS effects than the default theme and does not perform as smoothly on under powered hardware such as older Raspberry Pis. If you have a problem with Octodash performance please try using the default theme before reporting any issues. This theme performs well on a Raspbery Pi4 with a 7" LCD.
20 |
21 | ###### Theme by theGarbz.
22 |
23 | ## Screenshots:
24 |
25 | 1. Printing with the Horizontal Progressbar:
26 |
27 | 
28 |
29 | 2. Printing with Circular Progressbar:
30 |
31 | 
32 |
33 | 3. File Selection:
34 |
35 | 
36 |
37 | 4. Pre-print screen:
38 |
39 | 
40 |
41 | 5. Adjustments mid print:
42 |
43 | 
44 |
45 | 6. Errors:
46 |
47 | 
48 |
49 | 7. Main Screen:
50 |
51 | 
52 |
53 | 8. Settings:
54 |
55 | 
56 |
57 | ### Version History:
58 |
59 | **v0.4:**
60 |
61 | - Fix for Layer display code - Reenables the icon in place of text.
62 | - Fixed settings items running off the edge of the screen.
63 | - Fxied settings scroll bars so they no longer run off the bottom of the screen.
64 |
65 | **v0.3:**
66 |
67 | - Temporary change in Layer display code to suit new Layer-Progress Component.
68 |
69 | **v0.2:**
70 |
71 | - Fixed bug which prevented the filament weight from being shown in the filament selector.
72 |
73 | **v0.1:**
74 |
75 | - Initial Issue
76 |
--------------------------------------------------------------------------------
/themes/theGarbz/Focus/screenshots/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Focus/screenshots/icon.png
--------------------------------------------------------------------------------
/themes/theGarbz/Focus/screenshots/screenshot_adjust.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Focus/screenshots/screenshot_adjust.png
--------------------------------------------------------------------------------
/themes/theGarbz/Focus/screenshots/screenshot_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Focus/screenshots/screenshot_error.png
--------------------------------------------------------------------------------
/themes/theGarbz/Focus/screenshots/screenshot_filequeue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Focus/screenshots/screenshot_filequeue.png
--------------------------------------------------------------------------------
/themes/theGarbz/Focus/screenshots/screenshot_fileselect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Focus/screenshots/screenshot_fileselect.png
--------------------------------------------------------------------------------
/themes/theGarbz/Focus/screenshots/screenshot_main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Focus/screenshots/screenshot_main.png
--------------------------------------------------------------------------------
/themes/theGarbz/Focus/screenshots/screenshot_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Focus/screenshots/screenshot_menu.png
--------------------------------------------------------------------------------
/themes/theGarbz/Focus/screenshots/screenshot_print.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Focus/screenshots/screenshot_print.png
--------------------------------------------------------------------------------
/themes/theGarbz/Focus/screenshots/screenshot_print2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Focus/screenshots/screenshot_print2.png
--------------------------------------------------------------------------------
/themes/theGarbz/Glanceable/README.md:
--------------------------------------------------------------------------------
1 | # Glanceable theme v0.2
2 |
3 | This variation on the standard theme includes a nearly full width horizontal progress bar during printing. This makes it far easier to see the progress of a print when glancing at the screen from a distance, hence the name "Glanceable theme".
4 |
5 | To install copy the custom-styles.css file into the octodash config folder:
6 |
7 | ```
8 | ~/.config/octodash/custom-styles.css
9 | ```
10 |
11 | ###### Theme by theGarbz.
12 |
13 | ## Screenshots:
14 |
15 | 1. Printing with the Horizontal Progressbar:
16 |
17 | 
18 |
19 | 2. Printing with Circular Progressbar:
20 |
21 | 
22 |
23 | ### Version History:
24 |
25 | **v0.2:**
26 |
27 | - Theme updated to suit new layer progress layout in Octodash 2.2.0
28 |
29 | **v0.1:**
30 |
31 | - Initial Issue
32 |
--------------------------------------------------------------------------------
/themes/theGarbz/Glanceable/custom-styles.css:
--------------------------------------------------------------------------------
1 | /**** Job-Info Resizes ****/
2 |
3 | app-job-status ~ app-printer-status .printer-status {
4 | position: absolute !important;
5 | top: 42vh !important;
6 | left: 0 !important;
7 | }
8 |
9 | app-job-status ~ app-printer-status .printer-status__set-value {
10 | margin-top: -2vh !important;
11 | font-size: 3.9vw !important;
12 | }
13 |
14 | /**** Job-Info Layer Progress ****/
15 |
16 | .height-indication {
17 | position: absolute !important;
18 | top: 66vh !important;
19 | left: 0 !important;
20 | font-size: 3vw !important;
21 | }
22 |
23 | .height-indication__current-height {
24 | font-size: 6vh !important;
25 | }
26 |
27 | .height-indication__total-height {
28 | font-size: 5vh !important;
29 | }
30 |
31 | /**** Job-Info Progress ****/
32 |
33 | .job-info__progress-bar {
34 | height: 8vh !important;
35 | border-radius: 1.7vw !important;
36 | }
37 |
38 | .job-info__progress-bar__wrapper {
39 | position: fixed !important;
40 | top: 77vh !important;
41 | left: 3vw !important;
42 | width: 92vw !important;
43 | height: 8vh !important;
44 | border-radius: 2vw !important;
45 | background-color: rgb(38 44 53 / 100%);
46 | border-style: solid !important;
47 | border-color: rgb(121 121 121);
48 | border-width: 2px !important;
49 | }
50 |
51 | #progress-preview-bar + .job-info__progress-percentage {
52 | margin-top: -0.3vh !important;
53 | margin-left: 0 !important;
54 | font-size: 6.7vh !important;
55 | font-weight: 500 !important;
56 | display: block !important;
57 | position: fixed !important;
58 | left: 43.4vw !important;
59 | top: 77vh !important;
60 | z-index: 1 !important;
61 | color: white !important;
62 | text-shadow: 1px 1px 4px black;
63 | visibility: visible !important;
64 | }
65 |
--------------------------------------------------------------------------------
/themes/theGarbz/Glanceable/screenshots/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Glanceable/screenshots/icon.png
--------------------------------------------------------------------------------
/themes/theGarbz/Glanceable/screenshots/screenshot_printing_circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Glanceable/screenshots/screenshot_printing_circle.png
--------------------------------------------------------------------------------
/themes/theGarbz/Glanceable/screenshots/screenshot_printing_straight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/UnchartedBull/OctoDash/220ba0904dbbdef6973210a51dd56917006b1b81/themes/theGarbz/Glanceable/screenshots/screenshot_printing_straight.png
--------------------------------------------------------------------------------
/themes/theGarbz/README.md:
--------------------------------------------------------------------------------
1 | # A collection of themes by Garbz
2 |
3 | ### [Focus](Focus/)
4 |
5 | This theme focuses on important information for the user. A variation on the NOX theme, it reduces the contrast of non-important elements and incorporates large very easy to see progress bars giving the user quick access to the most important of information.
6 |
7 | 
8 |
9 | ### [Big-Fingers](BigFingers/)
10 |
11 | A variation on the default theme with wider vertical scrollbars. This is optimised for smaller screens and makes navigating files and filaments easier.
12 |
13 | 
14 |
15 | ### [Glanceable](Glanceable/)
16 |
17 | A variation on the default theme which introduces the option of a horizontal progress bar during printing. This alternate view allows for easier view of print progress from a distance while retaining the look and feel of the rest of the default OctoDash theme.
18 |
19 | 
20 |
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./out-tsc/app",
5 | "types": ["@angular/localize"]
6 | },
7 | "files": ["src/main.ts"],
8 | "include": ["src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "module": "ESNext",
9 | "moduleResolution": "node",
10 | "emitDecoratorMetadata": true,
11 | "experimentalDecorators": true,
12 | "importHelpers": true,
13 | "target": "ES2022",
14 | "typeRoots": ["node_modules/@types"],
15 | "types": [],
16 | "lib": ["ES2022", "dom"],
17 | "esModuleInterop": true,
18 | "resolveJsonModule": true
19 | },
20 | "exclude": ["**/node_modules/**"]
21 | }
22 |
--------------------------------------------------------------------------------
/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "./out-tsc/spec",
5 | "types": ["jasmine", "node", "@angular/localize"]
6 | },
7 | "files": ["src/test.ts"],
8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------