github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/initial/initial.go (about)

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2016-2023, Filippov Alex
     4  //
     5  // This library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU Lesser General Public
     7  // License as published by the Free Software Foundation; either
     8  // version 3 of the License, or (at your option) any later version.
     9  //
    10  // This library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13  // Library General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public
    16  // License along with this library.  If not, see
    17  // <https://www.gnu.org/licenses/>.
    18  
    19  package initial
    20  
    21  import (
    22  	"context"
    23  	"encoding/hex"
    24  	"errors"
    25  	"fmt"
    26  	"go.uber.org/fx"
    27  	"gorm.io/gorm"
    28  
    29  	. "github.com/e154/smart-home/adaptors"
    30  	"github.com/e154/smart-home/api"
    31  	"github.com/e154/smart-home/common/apperr"
    32  	"github.com/e154/smart-home/common/encryptor"
    33  	"github.com/e154/smart-home/common/events"
    34  	"github.com/e154/smart-home/common/logger"
    35  	m "github.com/e154/smart-home/models"
    36  	_ "github.com/e154/smart-home/plugins"
    37  	"github.com/e154/smart-home/system/access_list"
    38  	"github.com/e154/smart-home/system/automation"
    39  	"github.com/e154/smart-home/system/bus"
    40  	"github.com/e154/smart-home/system/gate/client"
    41  	. "github.com/e154/smart-home/system/initial/assertions"
    42  	"github.com/e154/smart-home/system/initial/demo"
    43  	localMigrations "github.com/e154/smart-home/system/initial/local_migrations"
    44  	"github.com/e154/smart-home/system/logging_ws"
    45  	"github.com/e154/smart-home/system/media"
    46  	"github.com/e154/smart-home/system/scheduler"
    47  	"github.com/e154/smart-home/system/scripts"
    48  	"github.com/e154/smart-home/system/supervisor"
    49  	"github.com/e154/smart-home/system/terminal"
    50  	"github.com/e154/smart-home/system/validation"
    51  	"github.com/e154/smart-home/version"
    52  )
    53  
    54  var (
    55  	log = logger.MustGetLogger("initial")
    56  )
    57  
    58  // Initial ...
    59  type Initial struct {
    60  	adaptors        *Adaptors
    61  	scriptService   scripts.ScriptService
    62  	accessList      access_list.AccessListService
    63  	supervisor      supervisor.Supervisor
    64  	automation      automation.Automation
    65  	api             *api.Api
    66  	gateClient      *client.GateClient
    67  	validation      *validation.Validate
    68  	localMigrations *localMigrations.Migrations
    69  	demo            *demo.Demos
    70  	eventBus        bus.Bus
    71  	db              *gorm.DB
    72  }
    73  
    74  // NewInitial ...
    75  func NewInitial(lc fx.Lifecycle,
    76  	adaptors *Adaptors,
    77  	scriptService scripts.ScriptService,
    78  	accessList access_list.AccessListService,
    79  	supervisor supervisor.Supervisor,
    80  	automation automation.Automation,
    81  	api *api.Api,
    82  	gateClient *client.GateClient,
    83  	validation *validation.Validate,
    84  	_ *logging_ws.LoggingWs,
    85  	localMigrations *localMigrations.Migrations,
    86  	demo *demo.Demos,
    87  	_ *scheduler.Scheduler,
    88  	_ *media.Media,
    89  	db *gorm.DB,
    90  	eventBus bus.Bus,
    91  	_ *terminal.Terminal) *Initial {
    92  	initial := &Initial{
    93  		adaptors:        adaptors,
    94  		scriptService:   scriptService,
    95  		accessList:      accessList,
    96  		supervisor:      supervisor,
    97  		automation:      automation,
    98  		api:             api,
    99  		gateClient:      gateClient,
   100  		validation:      validation,
   101  		localMigrations: localMigrations,
   102  		demo:            demo,
   103  		db:              db,
   104  		eventBus:        eventBus,
   105  	}
   106  	lc.Append(fx.Hook{
   107  		OnStart: func(ctx context.Context) (err error) {
   108  			return initial.Start(ctx)
   109  		},
   110  		OnStop: func(ctx context.Context) (err error) {
   111  			return initial.Shutdown(ctx)
   112  		},
   113  	})
   114  	return initial
   115  }
   116  
   117  // InstallDemoData ...
   118  func (n *Initial) InstallDemoData() {
   119  
   120  	log.Info("install demo data")
   121  
   122  	tx := n.adaptors.Begin()
   123  
   124  	// install demo
   125  	_ = n.demo.InstallByName(context.TODO(), tx, "example1")
   126  
   127  	_ = tx.Commit()
   128  
   129  	log.Info("complete")
   130  }
   131  
   132  // checkForUpgrade ...
   133  func (n *Initial) checkForUpgrade() {
   134  
   135  	defer func() {
   136  		fmt.Println("")
   137  	}()
   138  
   139  	const name = "initialVersion"
   140  	v, err := n.adaptors.Variable.GetByName(context.Background(), name)
   141  	if err != nil {
   142  
   143  		if errors.Is(err, apperr.ErrNotFound) {
   144  			v = m.Variable{
   145  				Name:   name,
   146  				Value:  fmt.Sprintf("%d", 1),
   147  				System: true,
   148  			}
   149  			err = n.adaptors.Variable.Add(context.Background(), v)
   150  			So(err, ShouldBeNil)
   151  		}
   152  	}
   153  
   154  	oldVersion := v.Value
   155  	So(err, ShouldBeNil)
   156  
   157  	var currentVersion string
   158  	if currentVersion, err = n.localMigrations.Up(context.TODO(), n.adaptors, oldVersion); err != nil {
   159  		return
   160  	}
   161  
   162  	v.Value = currentVersion
   163  	err = n.adaptors.Variable.Update(context.Background(), v)
   164  	So(err, ShouldBeNil)
   165  }
   166  
   167  // Start ...
   168  func (n *Initial) Start(ctx context.Context) (err error) {
   169  	n.checkForUpgrade()
   170  
   171  	variable, _ := n.adaptors.Variable.GetByName(ctx, "encryptor")
   172  	val, _ := hex.DecodeString(variable.Value)
   173  	encryptor.SetKey(val)
   174  
   175  	_ = n.eventBus.Subscribe("system/models/variables/+", n.eventHandler)
   176  	_ = n.eventBus.Subscribe("system", n.eventHandler)
   177  
   178  	_ = n.gateClient.Start()
   179  	_ = n.supervisor.Start(ctx)
   180  	_ = n.automation.Start()
   181  	go func() {
   182  		_ = n.api.Start()
   183  	}()
   184  	return
   185  }
   186  
   187  // Shutdown ...
   188  func (n *Initial) Shutdown(ctx context.Context) (err error) {
   189  	_ = n.eventBus.Unsubscribe("system/models/variables/+", n.eventHandler)
   190  	_ = n.eventBus.Unsubscribe("system", n.eventHandler)
   191  	_ = n.api.Shutdown(ctx)
   192  	return
   193  }
   194  
   195  func (n *Initial) eventHandler(_ string, message interface{}) {
   196  	switch v := message.(type) {
   197  	case events.EventGetServerVersion:
   198  		n.eventBus.Publish("system", events.EventServerVersion{
   199  			Common: events.Common{
   200  				Owner:     events.OwnerSystem,
   201  				SessionID: v.SessionID,
   202  				User:      v.User,
   203  			},
   204  			Version: version.GetVersion(),
   205  		})
   206  	case events.EventUpdatedVariableModel:
   207  		if v.Name == "timezone" && v.Value != "" {
   208  			log.Infof("update database timezone to: \"%s\"", v.Value)
   209  			n.db.Exec(`SET TIME ZONE '?';`, v.Value)
   210  		}
   211  	}
   212  }