Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reducing the time between captures won't take effect if timelapse.service is started with secondsBetweenCaptures CLI flag #27

Merged
merged 14 commits into from
Mar 31, 2021
Binary file modified bin/arm/go-raspberry-pi-timelapse
Binary file not shown.
26 changes: 15 additions & 11 deletions conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,26 @@ const (
)

var (
LogToFile = DefaultLogToFile
ListenAddress = DefaultListenAddress
StorageFolder = DefaultStorageFolder
LogToFile = DefaultLogToFile
ListenAddress = DefaultListenAddress
StorageFolder = DefaultStorageFolder
SecondsBetweenCaptures = DefaultSecondsBetweenCaptures
)

// TODO merge values coming from different sources: config, settings, CLI

// Update Updates configuration depending on the state of CLI flags provided.
func Update(listenAddress *string, storageAddress *string, logToFile *bool) {
if logToFile != nil {
LogToFile = *logToFile
// OverrideDefaultConfig Override default config values which were provided.
func OverrideDefaultConfig(listenAddressOverride *string, storageAddressOverride *string, logToFileOverride *bool, secondsBetweenCapturesOverride *int) {
if logToFileOverride != nil {
LogToFile = *logToFileOverride
}
if listenAddress != nil {
ListenAddress = *listenAddress
if listenAddressOverride != nil {
ListenAddress = *listenAddressOverride
}
if storageAddress != nil {
StorageFolder = *storageAddress
if storageAddressOverride != nil {
StorageFolder = *storageAddressOverride
}
if secondsBetweenCapturesOverride != nil {
SecondsBetweenCaptures = *secondsBetweenCapturesOverride
}
}
47 changes: 47 additions & 0 deletions conf/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package conf

import (
"encoding/json"
"errors"
)

const (
settingsFile = "timelapse-settings.db"
bucket = "settings"
settingsKey = "s"
)

var missingBucketError = errors.New("missing bucket")
var settingsNotFound = errors.New("settings not found")

var initialConfiguration = Settings{
DebugEnabled: false,
SecondsBetweenCaptures: SecondsBetweenCaptures,
OffsetWithinHour: DefaultOffsetWithinHour,
// Default resolution: 3280x2464 (8 MP). 66%: 2186x1642 (3.5 MP), 50%: 1640x1232 (2 MP)
PhotoResolutionWidth: 2186,
PhotoResolutionHeight: 1642,
PreviewResolutionWidth: 640,
PreviewResolutionHeight: 480,
RotateBy: 0,
ResolutionSetting: 0,
Quality: 100,
}

type Settings struct {
SecondsBetweenCaptures int
OffsetWithinHour int
PhotoResolutionWidth int
PhotoResolutionHeight int
PreviewResolutionWidth int
PreviewResolutionHeight int
RotateBy int
ResolutionSetting int
Quality int
DebugEnabled bool
}

func (s Settings) String() string {
jsonStr, _ := json.Marshal(s)
return string(jsonStr)
}
50 changes: 7 additions & 43 deletions conf/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package conf

import (
"encoding/json"
"errors"
"log"
"os"
"time"
Expand All @@ -11,54 +10,18 @@ import (
)

const (
settingsFile = "timelapse-settings.db"
bucket = "settings"
settingsKey = "s"
minSecondsBetweenCaptures = 60
boldIoTimeout = 1 * time.Second
)

var missingBucketError = errors.New("missing bucket")
var settingsNotFound = errors.New("settings not found")

var initialConfiguration = Settings{
DebugEnabled: false,
SecondsBetweenCaptures: DefaultSecondsBetweenCaptures,
OffsetWithinHour: DefaultOffsetWithinHour,
// Default resolution: 3280x2464 (8 MP). 66%: 2186x1642 (3.5 MP), 50%: 1640x1232 (2 MP)
PhotoResolutionWidth: 2186,
PhotoResolutionHeight: 1642,
PreviewResolutionWidth: 640,
PreviewResolutionHeight: 480,
RotateBy: 0,
ResolutionSetting: 0,
Quality: 100,
}

type Settings struct {
SecondsBetweenCaptures int
OffsetWithinHour int
PhotoResolutionWidth int
PhotoResolutionHeight int
PreviewResolutionWidth int
PreviewResolutionHeight int
RotateBy int
ResolutionSetting int
Quality int
DebugEnabled bool
}

func (s Settings) String() string {
jsonStr, _ := json.Marshal(s)
return string(jsonStr)
}

func LoadConfiguration() (*Settings, error) {
if areSettingsMissing(settingsFile) {
log.Println("Creating initial settings file..")
return WriteConfiguration(initialConfiguration)
}
log.Println("Settings file exists.")

db, err := bolt.Open(settingsFile, 0600, &bolt.Options{Timeout: 1 * time.Second})
db, err := bolt.Open(settingsFile, 0600, &bolt.Options{Timeout: boldIoTimeout})
defer db.Close()

val, exists, err := get(db, settingsKey)
Expand All @@ -73,17 +36,18 @@ func LoadConfiguration() (*Settings, error) {
var existingSettings Settings
err = json.Unmarshal([]byte(val), &existingSettings)

if existingSettings.SecondsBetweenCaptures < 60 {
if existingSettings.SecondsBetweenCaptures < minSecondsBetweenCaptures {
// Enforce min time between captures. this also protects for errors as a result of this being 0.
existingSettings.SecondsBetweenCaptures = 60
existingSettings.SecondsBetweenCaptures = minSecondsBetweenCaptures
}

return &existingSettings, err
}

// WriteConfiguration Persist the provided settings. Returns the updated settings or an error.
func WriteConfiguration(s Settings) (*Settings, error) {
log.Printf("Write configuration: %s\n", s.String())
db, err := bolt.Open(settingsFile, 0600, &bolt.Options{Timeout: 1 * time.Second})
db, err := bolt.Open(settingsFile, 0600, &bolt.Options{Timeout: boldIoTimeout})
defer db.Close()

if err != nil {
Expand Down
8 changes: 7 additions & 1 deletion conf/settings_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package conf

import (
"github.com/facebookgo/ensure"
"testing"

"github.com/facebookgo/ensure"
)

func TestAreSettingsMissing(t *testing.T) {
ensure.True(t, areSettingsMissing("non-existing-file"))
ensure.False(t, areSettingsMissing("settings_test.go"))
}

func TestSettingsToString(t *testing.T) {
expected := `{"SecondsBetweenCaptures":0,"OffsetWithinHour":0,"PhotoResolutionWidth":0,"PhotoResolutionHeight":0,"PreviewResolutionWidth":0,"PreviewResolutionHeight":0,"RotateBy":0,"ResolutionSetting":0,"Quality":0,"DebugEnabled":false}`
ensure.DeepEqual(t, expected, Settings{}.String())
}
20 changes: 20 additions & 0 deletions conf/valid/strict_validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package valid

import "github.com/ingojaeckel/go-raspberry-pi-timelapse/conf"

type strictValidator struct{}

func (s strictValidator) Validate(settings conf.Settings) error {
if settings.Quality < 1 || settings.Quality > 100 {
return errQualityOutOfBounds
}
if settings.SecondsBetweenCaptures < 1 {
return errSecondsBetweenCapturesOutOfBounds
}
if settings.OffsetWithinHour != -1 {
if settings.OffsetWithinHour < 0 || settings.OffsetWithinHour > 3599 {
return errOffsetWithinHourOutOfBounds
}
}
return nil
}
21 changes: 21 additions & 0 deletions conf/valid/validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package valid

import (
"errors"

"github.com/ingojaeckel/go-raspberry-pi-timelapse/conf"
)

var (
errQualityOutOfBounds = errors.New("Quality is out of bounds")
errOffsetWithinHourOutOfBounds = errors.New("Offset within hour is out of bounds")
errSecondsBetweenCapturesOutOfBounds = errors.New("Seconds between captures is out of bounds")
)

type Validator interface {
Validate(conf.Settings) error
}

func New() Validator {
return strictValidator{}
}
19 changes: 19 additions & 0 deletions conf/valid/validator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package valid

import (
"testing"

"github.com/facebookgo/ensure"
"github.com/ingojaeckel/go-raspberry-pi-timelapse/conf"
)

func TestNew(t *testing.T) {
ensure.Nil(t, New().Validate(conf.Settings{Quality: 100, SecondsBetweenCaptures: 60, OffsetWithinHour: 900}))
ensure.Nil(t, New().Validate(conf.Settings{Quality: 100, SecondsBetweenCaptures: 60, OffsetWithinHour: -1}))
}

func TestOutOfBounds(t *testing.T) {
ensure.NotNil(t, New().Validate(conf.Settings{Quality: 0}))
ensure.NotNil(t, New().Validate(conf.Settings{Quality: 100, SecondsBetweenCaptures: 0}))
ensure.NotNil(t, New().Validate(conf.Settings{Quality: 100, SecondsBetweenCaptures: 60, OffsetWithinHour: -2}))
}
23 changes: 13 additions & 10 deletions files/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"io"
"io/ioutil"
"log"
"os"
"sort"
)
Expand Down Expand Up @@ -77,27 +78,31 @@ func CanServeFile(path string, maxFileSizeBytes int64) (bool, error) {
// As a result this can only write as quickly as the content is being read.
func TarWithPipes(filePaths []string, pw *io.PipeWriter) error {
tw := tar.NewWriter(pw)
defer tw.Close()

for _, f := range filePaths {
info, err := os.Stat(f)
if err != nil {
return err
}
hdr, _ := tar.FileInfoHeader(info, info.Name())
if err = tw.WriteHeader(hdr); err != nil {
hdr := &tar.Header{
Name: f,
Size: info.Size(),
Mode: int64(info.Mode()),
ModTime: info.ModTime(),
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
content, err := ioutil.ReadFile(f)
file, err := os.Open(f)
if err != nil {
return err
}
if _, err := tw.Write(content); err != nil {
if _, err := io.Copy(tw, file); err != nil {
log.Println(err.Error())
return err
}
}
if err := tw.Close(); err != nil {
return err
}
return nil
}

Expand All @@ -106,6 +111,7 @@ func TarWithPipes(filePaths []string, pw *io.PipeWriter) error {
// As a result this can only write as quickly as the content is being read.
func ZipWithPipes(filePaths []string, pw *io.PipeWriter) error {
w := zip.NewWriter(pw)
defer w.Close()

for _, f := range filePaths {
info, err := os.Stat(f)
Expand All @@ -129,9 +135,6 @@ func ZipWithPipes(filePaths []string, pw *io.PipeWriter) error {
return err
}
}
if err := w.Close(); err != nil {
return err
}
return nil
}

Expand Down
24 changes: 16 additions & 8 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Grid from '@material-ui/core/Grid';
import HomeIcon from '@material-ui/icons/Home';
import SettingsIcon from '@material-ui/icons/Settings';
import TimelineIcon from '@material-ui/icons/Timeline';
import DescriptionIcon from '@material-ui/icons/Description';
import { Container, List, Avatar, ListItemAvatar, ListItem, CssBaseline } from '@material-ui/core';
import Switcher from './Switch';
import axios from 'axios';
Expand All @@ -24,43 +25,50 @@ function App() {
<React.Fragment>
<CssBaseline />
<Container fixed>
<Grid container spacing={2}>
<Grid item xs={1}>
<List>
<ListItem to={`${process.env.PUBLIC_URL}/`} component={NavLink}>
<Grid container spacing={0}>
<Grid item xs={2} alignItems="center">
<List disablePadding>
<ListItem to={`${process.env.PUBLIC_URL}/`} component={NavLink} disableGutters>
<ListItemAvatar>
<Avatar>
<HomeIcon />
</Avatar>
</ListItemAvatar>
</ListItem>
<ListItem to={`${process.env.PUBLIC_URL}/preview`} component={NavLink}>
<ListItem to={`${process.env.PUBLIC_URL}/preview`} component={NavLink} disableGutters>
<ListItemAvatar>
<Avatar>
<PhotoCamera />
</Avatar>
</ListItemAvatar>
</ListItem>
<ListItem to={`${process.env.PUBLIC_URL}/monitoring`} component={NavLink}>
<ListItem to={`${process.env.PUBLIC_URL}/monitoring`} component={NavLink} disableGutters>
<ListItemAvatar>
<Avatar>
<TimelineIcon />
</Avatar>
</ListItemAvatar>
</ListItem>
<ListItem to={`${process.env.PUBLIC_URL}/settings`} component={NavLink}>
<ListItem to={`${process.env.PUBLIC_URL}/settings`} component={NavLink} disableGutters>
<ListItemAvatar>
<Avatar>
<SettingsIcon />
</Avatar>
</ListItemAvatar>
</ListItem>
<ListItem to={`${process.env.PUBLIC_URL}/logs`} component={NavLink} disableGutters>
<ListItemAvatar>
<Avatar>
<DescriptionIcon />
</Avatar>
</ListItemAvatar>
</ListItem>
</List>
</Grid>
<Grid item xs={10}>
<Switcher />
</Grid>
<Grid item xs={10}>
<Grid item xs={12}>
<div className="footer">version: <a href={"https://github.com/ingojaeckel/go-raspberry-pi-timelapse/commit/" + process.env.REACT_APP_GIT_SHA}>{process.env.REACT_APP_GIT_SHA}</a></div>
</Grid>
</Grid>
Expand Down
Loading