Category: Blog

  • zwave-core

    This project is closing its doors. With the advent of https://github.com/cgarwood/homeassistant-zwave_mqtt using https://github.com/OpenZWave/qt-openzwave a.k.a. ozwdaemon there is not much more I could wish for (let’s say they stole, my idea if anyone is asking πŸ˜„). In good open-source fashion I’ll better invest into these projects instead of re-inventing the wheel. It was a short, but fun ride…

    to be an archive soon


    Z-Wave Core – Complete, flexible, pluggable

    • Maintain, troubleshoot, investigate the details of your Z-Wave network
    • You are simply looking to (finally) replace ozwcp to maintain your network hassle-free? here you go!
    • Access to your network from any automation with REST or publish your network as MQTT! (not yet available)

    Buy Me A Coffee Or Beer

    The current status:

    • Z-Wave backend / REST API: beta

    • Browser Frontend: alpha

    • exposing your Z-Wave network as MQTT client alpha, not for daily use, yet

      • Activating mqtt (within HA) and its discovery, allows full GUI based configuration
      • Within zwave-core just run start.py once the network is ready, visit: http://<my_zwave_core_ip>:8123/mqtt

    Quickstart

    1. start somewhere, e.g., cd /tmp

    2. git clone git@github.com:daringer/zwave-core.git

    3. make sure you’ve python > 3.x installed

    4. install venv using pip: python -m pip install venv

    5. create a fresh virtual environment and activate it:

    python -m venv /tmp/zwave_core_venv
    source /tmp/zwave_core_venv/bin/activate
    1. install the requirements:
    cd /tmp/zwave-core
    python -m pip install -r requirements.txt
    1. locate your essential Z-Wave files and copy them into the zwave-core directory (this makes sure that you’ll change nothing within your current Z-Wave using tool):
    # for example for home-assistant
    cd ~/.homeassistant
    cp zwcfg_0xf5b17667.xml pyozw.sqlite zwscene.xml options.xml /tmp/zwave-core/
    1. locate (and determine) your openzwave installation (flavor), see at openzwave. For me, I prefer the system-wide as having open-zwave installed through my pacman-ager. So for me its: /etc/openzwave

    2. check if your controller serial device (something like /dev/ttyACM0) is writeable

    3. simply run:

    cd /tmp/zwave-core
    python zwave_core/start.py
    1. start a browser and visit: 127.0.0.1:5000/frontend

    Now to start the network, just make sure that the 4 input fields in the top left are correct. Controller device /dev/ttyACM0, openzwave database directory /etc/openzwave, user-dir is our current workdir, where we copied the openzwave files to for safety: ..

    Once done click “ON” in the top left corner and wait while watching the event-log being populated by the tasks the controller is executing…

    The Vision

    ZWave-core shall—as the name suggests—be the core component for the interaction with Z-Wave devices and the network. Providing a highly transparent and accurate interface into the Z-Wave protocol and not hiding any detail. Using a REST API this transparency is delivered in a platform independent, easy accessible way.

    A dynamic, web-based and reactive interface is exposing this REST API to the browser, serving as a clear window into the Z-Wave world. In order to transport the high dynamic of the Z-Wave network, websocket-based push is a must allowing the receiver side to not miss any information within the network with the smallest possible latency.

    In contrast to this plain, bare-metal approach the major distinguishing feature will be the MQTT layer, which introduces a abstraction layer between any home-automation system and the Z-Wave network in order to seperate what does not belong together: physical and functional layers. While the former is deployable standalone, without prior knowledge of the surroundings despite the Z-Wave network itself, the latter has the freedom to not care about the physical layer at all and focus on the functional, end-user focussed challenges. So, in simple words zwave-core provides a Z-Wave to IP bridge allowing simplicity in Z-Wave management bridging the gap between WiFi and the ISM band.

    REST API

    URL Methods Description
    /net GET
    /net/actions GET
    /net/action/action POST
    /net/ctrl/actions GET
    /net/ctrl/action/action POST
    /net/opts GET,DELETE,PATCH,POST
    /net/signals GET
    /nodes GET
    /node/int:node_id GET,PATCH
    /node/int:node_id/value/int:value_id GET,POST
    /node/actions GET
    /node/int:node_id/action/action POST
    /toc GET

    Browser Frontend

    127.0.0.1:5000/frontend

    MQTT

    There are several ways to access the zwave nodes and entities.

    • zwave/raw/<node_id>/# will provide you an extensive list about all lan-devices, including c onfigs, read-only, …
    • homeassistant/<component type (e.g., sensor)>/<node_id>/state#' will be populated automaticly
    • there will later also be the option to change how a specific device/entity is exposed to the outer world (providing the means to realize fixes/abstraction, alredy within zwave-core to keep home-assistant free from unneeded clutter.

    Status

    Documentation

    Realizations

    Contribute

    Screenshots


    full node view, any possible detail on one screen, directly editable, instant feedback, websocket driven event log in the top right corner


    controller view, less configuration, full ajax frontend (oczwp-replacement), full REST-api already available


    Visit original content creator repository https://github.com/daringer/zwave-core
  • HiveJack

    HiveJack

    This tool can be used during internal penetration testing to dump Windows credentials from an already-compromised host. It allows one to dump SYSTEM, SECURITY and SAM registry hives and once copied to the attacker machines provides an option to delete these files to clear the trace.

    Often, this is a repetitive process, once an attacker gets system-level access on the compromised host dumping hives values is the next step. Time is very valuable when it comes to internal penetration testing. HiveJack will save you plenty of time when it comes to dumping and deleting the files. You’ll never have to remember the command to perform the actions. πŸ˜‰

    image

    Files dumped in the c:\temp\ folder of the compromised host:

    image

    Files are successfully deleted from the compromised host upon clicking on the Delete Hives button:

    image

    Any suggestions or ideas for this tool are welcome – just tweet me on @ManiarViral

    A hive is a logical group of keys, subkeys, and values in the registry that has a set of supporting files loaded into memory when the operating system is started or a user logs in.

    Registry files have the following two formats:

    • Standard format: Supported from Windows 2000, also supported in the later versions of the Windows for backward compatibility
    • Latest format: Supported starting with Windows XP

    HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE\SAM, HKEY_LOCAL_MACHINE\Security, and HKEY_USERS.DEFAULT; all other hives use the latest format.

    During an internal penetration test, the attacker often wants to perform a lateral movement from one host to the other. To move from one host to the other attacker often requires account credentials. Using HiveJack attacker would be able to gather credentials via system hives.

    HiveJack is useful once the attacker has successfully gained local admin or system privileges on one of the compromised hosts. To further gain access within the network attacker can use registry hives. Dumping these hives would allow an attacker to capture system users’ password hashes.Β 

    Upon dumping the registry hives and pulling it on the attacking box one can use a tool such as secretsdump available here: https://github.com/SecureAuthCorp/impacket/blob/master/examples/secretsdump.py

    image

    Once the password hashes are obtained it opens the doors to a variety of attacks such as pass-the-hash, spraying or password cracking to perform a lateral movement within the network.

    When hive files are copied to the attacking machine it is a good practice to delete the files from the temp folder to avoid leaking of sensitive files or cleaning the traces.

    Quick Tip

    It is a good practice to check the C:\Windows\repair\ location to obtain the SAM and SYSTEM files to avoid detection from EDR solutions. However, this directory contains outdated copies of the original C:\Windows\System32\config\ files so it might not reflect the current users’ credentials. However, if the passwords are cracked it may be useful to know any password patterns such as Winter2020 or Summer2020

    How do I use this?

    Method 1:
    Use a HiveJack.exe file from the release section (https://github.com/Viralmaniar/HiveJack/releases/download/v1.0/HiveJack.exe) and run it on the compromised host. The hives will get stored at the c:\temp\ folder.

    Method 2:
    Open the solution using Visual Studio and look at the code to build the solution.

    Note: Please make sure you have a temp folder in the ‘C:’ Drive of the compromised host before dumping the registry hives.

    Questions?

    Twitter: https://twitter.com/maniarviral
    LinkedIn: https://au.linkedin.com/in/viralmaniar

    Contribution & License

    MIT License

    Copyright (c) 2020 Viral Maniar

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    Any suggestions or ideas for this tool are welcome – just tweet me on @ManiarViral @PreemptiveCyber

    Visit original content creator repository https://github.com/Viralmaniar/HiveJack
  • CS651-UW-Project

    CS651 Project

    This project is for the CS651 final project.

    We were given a task to apply the big data tools that were learnt in the class combined with some machine learning models to solve a real world problem.

    We decided to predict the winner of US presidential elections 2020 out of the two contestants(Trump,Biden).

    As it is a binary classification problem, we used several binary classifiers such as Logistic Regression, Naive Bayes Model for prediction. We also applied sentimental analysis and figured out from the tweets who’ll win the presidential elections.

    US Presidential Election 2020 Winner

    PACKAGES

    You need to install following packages first

    • sklearn
    • pandas
    • numpy
    • findspark
    • re
    • subprocess
    • sys
    • pyspark
    • tweepy
    • socket

    Go ahead and pip install above packages.

    pip install pyqt5
    pip install sklearn
    pip install pandas
    pip install numpy
    pip install dbConnect
    pip install sklearn
    pip install pandas
    pip install numpy 
    pip install findspark
    pip install re 
    pip install subprocess 
    pip install sys 
    pip install pyspark  
    pip install tweepy
    pip installsocket 
    

    To run the file simply

    Open two terminal if on MAC OS, if on Windows open Command Prompt or in UNIX open Command Prompt/Shell.

    In one terminal Run

    python stream_producer.py
    

    In the other terminal Run

    python stream_consumer.py
    

    DATA-GATHERING

    The data source was the tweets and they were collected in real-time from the twitter. As the data was huge we use a Big Data framework Spark Streaming to fetch the tweets in real time. We assigned a time window of 15 minutes for gathering the tweets.

    DATA PRE-PROCESSING

    In this step we look for any discrepancies in the dataset. As the tweets can contain unnecassary things , it should be cleaned and processed before passing it to a model.

    FEATURE-ENGINEERING

    As our problem dealt with text, we had only a single text feature, we didn’t need to remove any other features. The feature engineering we applied was to
    remove emojis, unwanted text, explain here more.

    MODEL SELECTION

    Our problem was a binary classification, so we used Logistic Regression & Naive Bayes Model.

    MODEL EVALUATION & COMPARISION

    Out of these two, Naive Bayes Model preformed better.

    MODEL DEPLOYMENT

    As this was a school project, we weren’t asked to deploy our model somewhere. If asked, we would’ve done that using Flask framework.

    Visit original content creator repository
    https://github.com/manishghop/CS651-UW-Project

  • terraform-oci-compute-instance

    Oracle Cloud Infrastructure Terraform Module for Compute Instance

    This module provides an easy way to launch compute instances with advanced settings and good practices embedded.

    On top of the compute instance capabilities, this module can also:

    • provision and attach additional Block Volumes to the instances,
    • assign a reserved public IP, instead of using Ephemeral,
    • assign a backup policy for boot volumes

    Please Note:

    All Oracle-provided images include firewall rules that allow only “root” on Linux instances or “Administrators” on Windows Server instances to make outgoing connections to the iSCSI network endpoints (169.254.0.2:3260, 169.254.2.0/24:3260) that serve the instance’s boot and block volumes.

    Oracle recommends that you do not use custom images without these rules unless you understand the security risks. See Compute Best Practices for recommendations on how to manage instances.

    Requirements

    The diagram below summarizes the required components and their respective versions to use this module.

    versions

    To enforce versions compatibility of both Terraform and the OCI provider, your root configuration should ideally include this block for version pinning:

    terraform {
      required_version = ">= 0.12.6"
      required_providers {
        oci = {
          version = ">= 4.0.0"
        }
      }
    }

    For detailed information about inputs and outputs, and potential sub-modules, see docs/terraformoptions.

    How to use this module

    See Oracle Cloud Infrastructure documentation to get started with the Oracle Cloud Infrastructure Terraform Provider.

    The examples folder contains detailed examples that shows how to use this module. The following code example creates an Oracle Cloud Infrastructure compute instance on AD-1 with an additional Block Volume attached:

    module "instance" {
      source = "oracle-terraform-modules/compute-instance/oci"
      instance_count             = 1 # how many instances do you want?
      ad_number                  = 1 # AD number to provision instances. If null, instances are provisionned in a rolling manner starting with AD1
      compartment_ocid           = var.compartment_ocid
      instance_display_name      = var.instance_display_name
      source_ocid                = var.source_ocid
      subnet_ocids               = var.subnet_ocids
      public_ip                  = var.public_ip # NONE, RESERVED or EPHEMERAL
      ssh_public_keys            = var.ssh_public_keys
      block_storage_sizes_in_gbs = [50]
      shape                      = var.shape
      instance_state             = var.instance_state # RUNNING or STOPPED
      boot_volume_backup_policy  = var.boot_volume_backup_policy # disabled, gold, silver or bronze
    }

    What’s coming next for this module?

    The current focus is to close the gap between this module and the provider’s capabilities. We started with a complete codebase update for HCL2 syntax compatibility, then adding support for Regional Subnets and lastly Flexible Shapes.

    We will continue to push in that direction with the goal of feature parity with the provider’s capabilities, as well as adding more features and integration points with other OCI services: Block Volume Backups, Secondary VNICs and IPs, etc …

    Given the dependency to Network and Storage for Compute Instances, this module is also a perfect place to illustrate module composition principles and how to reuse the other official Terraform OCI modules.

    Contributing

    This project is open source and maintained by Oracle.
    Oracle appreciates any contributions that are made by the open source community: raising issues, improving documentation, fixing bugs, or adding new features.

    Learn how to contribute.

    Folks who contributed with explanations, code, feedback, ideas, testing etc.

    License

    Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.

    Licensed under the Universal Permissive License 1.0 or Apache License 2.0.

    See LICENSE for more details.

    Visit original content creator repository https://github.com/oracle-terraform-modules/terraform-oci-compute-instance
  • ng-simple-state

    NgSimpleState Build Status Coverage Status NPM version Maintainability

    Simple state management in Angular with only Services and RxJS or Signal.

    Description

    Sharing state between components as simple as possible and leverage the good parts of component state and Angular’s dependency injection system.

    See the demos:

    Get Started

    Step 1: install ng-simple-state

    npm i ng-simple-state

    Step 2: Import provideNgSimpleState into your providers

    provideNgSimpleState has some global optional config defined by NgSimpleStateConfig interface:

    Option Description Default
    enableDevTool if true enable Redux DevTools browser extension for inspect the state of the store. false
    persistentStorage Set the persistent storage local or session. undefined
    comparator A function used to compare the previous and current state for equality. a === b

    Side note: each store can be override the global configuration implementing storeConfig() method (see “Override global config”).

    import { isDevMode } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    
    import { AppComponent } from './app.component';
    import { provideNgSimpleState } from 'ng-simple-state';
    
    bootstrapApplication(AppComponent, {
      providers: [
        provideNgSimpleState({
          enableDevTool: isDevMode(),
          persistentStorage: 'local'
        })
      ]
    });

    Step 3: Chose your store

    There are two type of store NgSimpleStateBaseRxjsStore based on RxJS BehaviorSubject and NgSimpleStateBaseSignalStore based on Angular Signal:

    RxJS Store

    This is an example for a counter store in a src/app/counter-store.ts file. Obviously, you can create every store you want with every complexity you need.

    1. Define your state interface, eg.:
    export interface CounterState {
        count: number;
    }
    1. Define your store service by extending NgSimpleStateBaseRxjsStore, eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseRxjsStore<CounterState> {
     
    }
    1. Implement initialState() and storeConfig() methods and provide the initial state of the store, eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseRxjsStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
      
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
    }
    1. Implement one or more selectors of the partial state you want, in this example selectCount() eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    import { Observable } from 'rxjs';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseRxjsStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
      
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
      selectCount(): Observable<number> {
        return this.selectState(state => state.count);
      }
    }
    1. Implement one or more actions for change the store state, in this example increment() and decrement() eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    import { Observable } from 'rxjs';
    
    export interface CounterState {
      count: number;
    }
    
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseRxjsStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
    
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
      selectCount(): Observable<number> {
        return this.selectState(state => state.count);
      }
    
      increment(increment: number = 1): void {
        this.setState(state => ({ count: state.count + increment }));
      }
    
      decrement(decrement: number = 1): void {
        this.setState(state => ({ count: state.count - decrement }));
      }
    }

    Step 3: Inject your store into the providers, eg.:

    import { Component } from '@angular/core';
    import { CounterStore } from './counter-store';
    
    @Component({
      selector: 'app-root',
      imports: [CounterStore]
    })
    export class AppComponent {
    
    }

    Step 4: Use your store into the components, eg.:

    import { Component, inject } from '@angular/core';
    import { Observable } from 'rxjs';
    import { CounterStore } from './counter-store';
    
    @Component({
      selector: 'app-root',
      imports: [CounterStore],
      template: `
      <h1>Counter: {{ counter$ | async }}</h1>
      <button (click)="counterStore.decrement()">Decrement</button>
      <button (click)="counterStore.resetState()">Reset</button>
      <button (click)="counterStore.increment()">Increment</button>
      `,
    })
    export class AppComponent {
      public counterStore = inject(CounterStore);
      public counter$: Observable<number> = this.counterStore.selectCount();
    }

    That’s all!

    alt text

    Manage component state without service

    If you want manage just a component state without make a new service, your component can extend directly NgSimpleStateBaseRxjsStore:

    import { Component } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore } from 'ng-simple-state';
    import { Observable } from 'rxjs';
    
    export interface CounterState {
        count: number;
    }
    
    @Component({
        selector: 'app-counter',
        template: `
            {{counter$ | async}}
            <button (click)="increment()">+</button>
            <button (click)="decrement()">-</button>
        `
    })
    export class CounterComponent extends NgSimpleStateBaseRxjsStore<CounterState> {
    
        public counter$: Observable<number> = this.selectState(state => state.count);
    
        storeConfig(): NgSimpleStateStoreConfig<CounterState> {
          return {
            storeName: 'CounterComponent'
          };
        }
    
        initialState(): CounterState {
            return {
                count: 0
            };
        }
    
        increment(): void {
            this.setState(state => ({ count: state.count + 1 }));
        }
    
        decrement(): void {
            this.setState(state => ({ count: state.count - 1 }));
        }
    }

    Override global config

    If you need to override the global configuration provided by provideNgSimpleState() you can implement storeConfig() and return a specific configuration for the single store, eg.:

    import { Injectable } from '@angular/core';
    import { NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseRxjsStore<CounterState> {
    
      override storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          persistentStorage: 'session', // persistentStorage can be 'session' or 'local' (default is localStorage)
          storeName: 'CounterStore2', // set a specific name for this store (must be be unique)
        }
      }
    }

    The options are defined by NgSimpleStateStoreConfig interface:

    Option Description Default
    enableDevTool if true enable Redux DevTools browser extension for inspect the state of the store. false
    storeName The store name. undefined
    persistentStorage Set the persistent storage local or session undefined
    comparator A function used to compare the previous and current state for equality. a === b

    Testing

    ng-simple-state is simple to test. Eg.:

    import { TestBed } from '@angular/core/testing';
    import { provideNgSimpleState } from 'ng-simple-state';
    import { CounterStore } from './counter-store';
    
    describe('CounterStore', () => {
    
      let counterStore: CounterStore;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          providers: [
            provideNgSimpleState({
              enableDevTool: false
            }),
            CounterStore
          ]
        });
    
        counterStore = TestBed.inject(CounterStore);
      });
    
      it('initialState', () => {
        expect(counterStore.getCurrentState()).toEqual({ count: 0 });
      });
    
      it('increment', () => {
        counterStore.increment();
        expect(counterStore.getCurrentState()).toEqual({ count: 1 });
      });
    
      it('decrement', () => {
        counterStore.decrement();
        expect(counterStore.getCurrentState()).toEqual({ count: -1 });
      });
    
      it('selectCount', (done) => {
        counterStore.selectCount().subscribe(value => {
          expect(value).toBe(0);
          done();
        });
      });
    
    });

    Example: array store

    This is an example for a todo list store in a src/app/todo-store.ts file.

    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore } from 'ng-simple-state';
    import { Observable } from 'rxjs';
    
    export interface Todo {
      id: number;
      name: string;
      completed: boolean;
    }
    
    export type TodoState = Array<Todo>;
    
    @Injectable()
    export class TodoStore extends NgSimpleStateBaseRxjsStore<TodoState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'TodoStore'
        };
      }
    
      initialState(): TodoState {
        return [];
      }
    
      add(todo: Omit<Todo, 'id'>): void {
        this.setState(state =>  [...state, {...todo, id: Date.now()}]);
      }
    
      delete(id: number): void {
        this.setState(state => state.filter(item => item.id !== id) );
      }
    
      setComplete(id: number, completed: boolean = true): void {
        this.setState(state => state.map(item => item.id === id ? {...item, completed} : item) );
      }
    }

    usage:

    import { Component, inject } from '@angular/core';
    import { Observable } from 'rxjs';
    import { Todo, TodoStore } from './todo-store';
    
    @Component({
      selector: 'app-root',
      template: `
        <input #newTodo> <button (click)="todoStore.add({name: newTodo.value, completed: false})">Add todo</button>
        <ol>
          @for(todo of todoList$ | async; track todo.id) {
            <li>
                @if(todo.completed) {
                  βœ…
                } 
                {{ todo.name }} 
                <button (click)="todoStore.setComplete(todo.id, !todo.completed)">Mark as {{ todo.completed ? 'Not completed' : 'Completed' }}</button> 
                <button (click)="todoStore.delete(todo.id)">Delete</button>
            </li>
          }
        </ol>
      `,
      providers: [TodoStore]
    })
    export class AppComponent {
      public todoStore = inject(TodoStore);
      public todoList$: Observable<Todo[]> = this.todoStore.selectState();
    }

    NgSimpleStateBaseRxjsStore API

    @Injectable()
    @Directive()
    export abstract class NgSimpleStateBaseRxjsStore<S extends object | Array<any>> implements OnDestroy {
    
        /**
         * Return the observable of the state
         * @returns Observable of the state
         */
        public get state(): BehaviorSubject<S>;
    
        /**
         * When you override this method, you have to call the `super.ngOnDestroy()` method in your `ngOnDestroy()` method.
         */
        ngOnDestroy(): void;
    
        /**
         * Reset store to first loaded store state:
         *  - the last saved state
         *  - otherwise the initial state provided from `initialState()` method.
         */
        resetState(): boolean;
    
        /**
         * Restart the store to initial state provided from `initialState()` method
         */
        restartState(): boolean;
    
        /**
         * Override this method for set a specific config for the store
         * @returns NgSimpleStateStoreConfig
         */
        storeConfig(): NgSimpleStateStoreConfig<S>;
    
        /**
         * Set into the store the initial state
         * @returns The state object
         */
        initialState(): S;
    
        /**
         * Select a store state
         * @param selectFn State selector (if not provided return full state)
         * @param comparator A function used to compare the previous and current state for equality. Defaults to a `===` check.
         * @returns Observable of the selected state
         */
        selectState<K>(selectFn?: (state: Readonly<S>) => K, comparator?: (previous: K, current: K) => boolean): Observable<K>;
    
        /**
         * Return the current store state (snapshot)
         * @returns The current state
         */
        getCurrentState(): Readonly<S>;
    
        /**
         * Return the first loaded store state:
         * the last saved state
         * otherwise the initial state provided from `initialState()` method.
         * @returns The first state
         */
        getFirstState(): Readonly<S> | null;
    
        /**
         * Set a new state
         * @param newState New state
         * @param actionName The action label into Redux DevTools (default is parent function name)
         * @returns True if the state is changed
         */
        setState(newState: Partial<S>, actionName?: string): boolean;
        /**
         * Set a new state
         * @param selectFn State reducer
         * @param actionName The action label into Redux DevTools (default is parent function name)
         * @returns True if the state is changed
         */
        setState(stateFn: NgSimpleStateSetState<S>, actionName?: string): boolean;
    }

    Signal Store

    This is an example for a counter store in a src/app/counter-store.ts file. Obviously, you can create every store you want with every complexity you need.

    1. Define your state interface, eg.:
    export interface CounterState {
        count: number;
    }
    1. Define your store service by extending NgSimpleStateBaseSignalStore, eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseSignalStore } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseSignalStore<CounterState> {
     
    }
    1. Implement initialState() and storeConfig() methods and provide the initial state of the store, eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseSignalStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseSignalStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
      
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
    }
    1. Implement one or more selectors of the partial state you want, in this example selectCount() eg.:
    import { Injectable, Signal } from '@angular/core';
    import { NgSimpleStateBaseSignalStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseSignalStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
      
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
      selectCount(): Signal<number> {
        return this.selectState(state => state.count);
      }
    }
    1. Implement one or more actions for change the store state, in this example increment() and decrement() eg.:
    import { Injectable, Signal } from '@angular/core';
    import { NgSimpleStateBaseSignalStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    export interface CounterState {
      count: number;
    }
    
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseSignalStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
    
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
      selectCount(): Signal<number> {
        return this.selectState(state => state.count);
      }
    
      increment(increment: number = 1): void {
        this.setState(state => ({ count: state.count + increment }));
      }
    
      decrement(decrement: number = 1): void {
        this.setState(state => ({ count: state.count - decrement }));
      }
    }

    Step 3: Inject your store into the providers, eg.:

    import { Component } from '@angular/core';
    import { CounterStore } from './counter-store';
    
    @Component({
      selector: 'app-root',
      imports: [CounterStore]
    })
    export class AppComponent {
    
    }

    Step 4: Use your store into the components, eg.:

    import { Component, Signal, inject } from '@angular/core';
    import { CounterStore } from './counter-store';
    
    @Component({
      selector: 'app-root',
      template: `
      <h1>Counter: {{ counterSig() }}</h1>
      <button (click)="counterStore.decrement()">Decrement</button>
      <button (click)="counterStore.resetState()">Reset</button>
      <button (click)="counterStore.increment()">Increment</button>
      `,
    })
    export class AppComponent {
      public counterStore = inject(CounterStore);
      public counterSig: Signal<number> = this.counterStore.selectCount();
    }

    That’s all!

    alt text

    Manage component state without service

    If you want manage just a component state without make a new service, your component can extend directly NgSimpleStateBaseSignalStore:

    import { Component, Signal } from '@angular/core';
    import { NgSimpleStateBaseSignalStore } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
    
    @Component({
        selector: 'app-counter',
        template: `
            {{counterSig()}}
            <button (click)="increment()">+</button>
            <button (click)="decrement()">-</button>
        `
    })
    export class CounterComponent extends NgSimpleStateBaseSignalStore<CounterState> {
    
        public counterSig: Signal<number> = this.selectState(state => state.count);
    
        storeConfig(): NgSimpleStateStoreConfig<CounterState> {
          return {
            storeName: 'CounterComponent'
          };
        }
    
        initialState(): CounterState {
            return {
                count: 0
            };
        }
    
        increment(): void {
            this.setState(state => ({ count: state.count + 1 }));
        }
    
        decrement(): void {
            this.setState(state => ({ count: state.count - 1 }));
        }
    }

    Override global config

    If you need to override the global configuration provided by provideNgSimpleState() you can implement storeConfig() and return a specific configuration for the single store, eg.:

    import { Injectable } from '@angular/core';
    import { NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseSignalStore<CounterState> {
    
      override storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          persistentStorage: 'session', // persistentStorage can be 'session' or 'local' (default is localStorage)
          storeName: 'CounterStore2', // set a specific name for this store (must be be unique)
        }
      }
    }

    The options are defined by NgSimpleStateStoreConfig interface:

    Option Description Default
    enableDevTool if true enable Redux DevTools browser extension for inspect the state of the store. false
    storeName The store name. undefined
    persistentStorage Set the persistent storage local or session undefined
    comparator A function used to compare the previous and current state for equality. a === b

    Testing

    ng-simple-state is simple to test. Eg.:

    import { TestBed } from '@angular/core/testing';
    import { provideNgSimpleState } from 'ng-simple-state';
    import { CounterStore } from './counter-store';
    
    describe('CounterStore', () => {
    
      let counterStore: CounterStore;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          providers: [
            provideNgSimpleState({
              enableDevTool: false
            }),
            CounterStore
          ]
        });
    
        counterStore = TestBed.inject(CounterStore);
      });
    
      it('initialState', () => {
        expect(counterStore.getCurrentState()).toEqual({ count: 0 });
      });
    
      it('increment', () => {
        counterStore.increment();
        expect(counterStore.getCurrentState()).toEqual({ count: 1 });
      });
    
      it('decrement', () => {
        counterStore.decrement();
        expect(counterStore.getCurrentState()).toEqual({ count: -1 });
      });
    
      it('selectCount', () => {
        const valueSig = counterStore.selectCount();
        expect(valueSig()).toBe(0);
      });
    
    });

    Example: array store

    This is an example for a todo list store in a src/app/todo-store.ts file.

    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseSignalStore } from 'ng-simple-state';
    
    export interface Todo {
      id: number;
      name: string;
      completed: boolean;
    }
    
    export type TodoState = Array<Todo>;
    
    @Injectable()
    export class TodoStore extends NgSimpleStateBaseSignalStore<TodoState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'TodoStore'
        };
      }
    
      initialState(): TodoState {
        return [];
      }
    
      add(todo: Omit<Todo, 'id'>): void {
        this.setState(state =>  [...state, {...todo, id: Date.now()}]);
      }
    
      delete(id: number): void {
        this.setState(state => state.filter(item => item.id !== id) );
      }
    
      setComplete(id: number, completed: boolean = true): void {
        this.setState(state => state.map(item => item.id === id ? {...item, completed} : item) );
      }
    }

    usage:

    import { Component, Signal, inject } from '@angular/core';
    import { Todo, TodoStore } from './todo-store';
    
    @Component({
      selector: 'app-root',
      template: `
        <input #newTodo> <button (click)="todoStore.add({name: newTodo.value, completed: false})">Add todo</button>
        <ol>
          @for(todo of todoListSig(); track todo.id) {
            <li>
                @if(todo.completed) {
                  βœ…
                }
                {{ todo.name }} 
                <button (click)="todoStore.setComplete(todo.id, !todo.completed)">Mark as {{ todo.completed ? 'Not completed' : 'Completed' }}</button> 
                <button (click)="todoStore.delete(todo.id)">Delete</button>
            </li>
          }
        </ol>
      `,
      providers: [TodoStore]
    })
    export class AppComponent {
      public todoStore = inject(TodoStore);
      public todoListSig: Signal<Todo[]> = this.todoStore.selectState();
    }

    NgSimpleStateBaseSignalStore API

    @Injectable()
    @Directive()
    export abstract class NgSimpleStateBaseSignalStore<S extends object | Array<any>> implements OnDestroy {
    
        /**
         * Return the Signal of the state
         * @returns Signal of the state
         */
        public get state(): Signal<S>;
    
        /**
         * When you override this method, you have to call the `super.ngOnDestroy()` method in your `ngOnDestroy()` method.
         */
        ngOnDestroy(): void;
    
        /**
         * Reset store to first loaded store state:
         *  - the last saved state
         *  - otherwise the initial state provided from `initialState()` method.
         */
        resetState(): boolean;
    
        /**
         * Restart the store to initial state provided from `initialState()` method
         */
        restartState(): boolean;
    
        /**
         * Override this method for set a specific config for the store
         * @returns NgSimpleStateStoreConfig
         */
        storeConfig(): NgSimpleStateStoreConfig<S>;
    
        /**
         * Set into the store the initial state
         * @returns The state object
         */
        initialState(): S;
    
        /**
         * Select a store state
         * @param selectFn State selector (if not provided return full state)
         * @param comparator A function used to compare the previous and current state for equality. Defaults to a `===` check.
         * @returns Signal of the selected state
         */
        selectState<K>(selectFn?: (state: Readonly<S>) => K, comparator?: (previous: K, current: K) => boolean): Signal<K>;
    
        /**
         * Return the current store state (snapshot)
         * @returns The current state
         */
        getCurrentState(): Readonly<S>;
    
        /**
         * Return the first loaded store state:
         * the last saved state
         * otherwise the initial state provided from `initialState()` method.
         * @returns The first state
         */
        getFirstState(): Readonly<S> | null;
    
        /**
         * Set a new state
         * @param newState New state
         * @param actionName The action label into Redux DevTools (default is parent function name)
         * @returns True if the state is changed
         */
        setState(newState: Partial<S>, actionName?: string): boolean;
        /**
         * Set a new state
         * @param selectFn State reducer
         * @param actionName The action label into Redux DevTools (default is parent function name)
         * @returns True if the state is changed
         */
        setState(stateFn: NgSimpleStateSetState<S>, actionName?: string): boolean; 
    }

    Alternatives

    Aren’t you satisfied? there are some valid alternatives:

    Support

    This is an open-source project. Star this repository, if you like it, or even donate. Thank you so much!

    My other libraries

    I have published some other Angular libraries, take a look:

    Visit original content creator repository https://github.com/nigrosimone/ng-simple-state
  • ng-simple-state

    NgSimpleState Build Status Coverage Status NPM version Maintainability

    Simple state management in Angular with only Services and RxJS or Signal.

    Description

    Sharing state between components as simple as possible and leverage the good parts of component state and Angular’s dependency injection system.

    See the demos:

    Get Started

    Step 1: install ng-simple-state

    npm i ng-simple-state

    Step 2: Import provideNgSimpleState into your providers

    provideNgSimpleState has some global optional config defined by NgSimpleStateConfig interface:

    Option Description Default
    enableDevTool if true enable Redux DevTools browser extension for inspect the state of the store. false
    persistentStorage Set the persistent storage local or session. undefined
    comparator A function used to compare the previous and current state for equality. a === b

    Side note: each store can be override the global configuration implementing storeConfig() method (see “Override global config”).

    import { isDevMode } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    
    import { AppComponent } from './app.component';
    import { provideNgSimpleState } from 'ng-simple-state';
    
    bootstrapApplication(AppComponent, {
      providers: [
        provideNgSimpleState({
          enableDevTool: isDevMode(),
          persistentStorage: 'local'
        })
      ]
    });

    Step 3: Chose your store

    There are two type of store NgSimpleStateBaseRxjsStore based on RxJS BehaviorSubject and NgSimpleStateBaseSignalStore based on Angular Signal:

    RxJS Store

    This is an example for a counter store in a src/app/counter-store.ts file. Obviously, you can create every store you want with every complexity you need.

    1. Define your state interface, eg.:
    export interface CounterState {
        count: number;
    }
    1. Define your store service by extending NgSimpleStateBaseRxjsStore, eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseRxjsStore<CounterState> {
     
    }
    1. Implement initialState() and storeConfig() methods and provide the initial state of the store, eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseRxjsStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
      
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
    }
    1. Implement one or more selectors of the partial state you want, in this example selectCount() eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    import { Observable } from 'rxjs';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseRxjsStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
      
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
      selectCount(): Observable<number> {
        return this.selectState(state => state.count);
      }
    }
    1. Implement one or more actions for change the store state, in this example increment() and decrement() eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    import { Observable } from 'rxjs';
    
    export interface CounterState {
      count: number;
    }
    
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseRxjsStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
    
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
      selectCount(): Observable<number> {
        return this.selectState(state => state.count);
      }
    
      increment(increment: number = 1): void {
        this.setState(state => ({ count: state.count + increment }));
      }
    
      decrement(decrement: number = 1): void {
        this.setState(state => ({ count: state.count - decrement }));
      }
    }

    Step 3: Inject your store into the providers, eg.:

    import { Component } from '@angular/core';
    import { CounterStore } from './counter-store';
    
    @Component({
      selector: 'app-root',
      imports: [CounterStore]
    })
    export class AppComponent {
    
    }

    Step 4: Use your store into the components, eg.:

    import { Component, inject } from '@angular/core';
    import { Observable } from 'rxjs';
    import { CounterStore } from './counter-store';
    
    @Component({
      selector: 'app-root',
      imports: [CounterStore],
      template: `
      <h1>Counter: {{ counter$ | async }}</h1>
      <button (click)="counterStore.decrement()">Decrement</button>
      <button (click)="counterStore.resetState()">Reset</button>
      <button (click)="counterStore.increment()">Increment</button>
      `,
    })
    export class AppComponent {
      public counterStore = inject(CounterStore);
      public counter$: Observable<number> = this.counterStore.selectCount();
    }

    That’s all!

    alt text

    Manage component state without service

    If you want manage just a component state without make a new service, your component can extend directly NgSimpleStateBaseRxjsStore:

    import { Component } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore } from 'ng-simple-state';
    import { Observable } from 'rxjs';
    
    export interface CounterState {
        count: number;
    }
    
    @Component({
        selector: 'app-counter',
        template: `
            {{counter$ | async}}
            <button (click)="increment()">+</button>
            <button (click)="decrement()">-</button>
        `
    })
    export class CounterComponent extends NgSimpleStateBaseRxjsStore<CounterState> {
    
        public counter$: Observable<number> = this.selectState(state => state.count);
    
        storeConfig(): NgSimpleStateStoreConfig<CounterState> {
          return {
            storeName: 'CounterComponent'
          };
        }
    
        initialState(): CounterState {
            return {
                count: 0
            };
        }
    
        increment(): void {
            this.setState(state => ({ count: state.count + 1 }));
        }
    
        decrement(): void {
            this.setState(state => ({ count: state.count - 1 }));
        }
    }

    Override global config

    If you need to override the global configuration provided by provideNgSimpleState() you can implement storeConfig() and return a specific configuration for the single store, eg.:

    import { Injectable } from '@angular/core';
    import { NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseRxjsStore<CounterState> {
    
      override storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          persistentStorage: 'session', // persistentStorage can be 'session' or 'local' (default is localStorage)
          storeName: 'CounterStore2', // set a specific name for this store (must be be unique)
        }
      }
    }

    The options are defined by NgSimpleStateStoreConfig interface:

    Option Description Default
    enableDevTool if true enable Redux DevTools browser extension for inspect the state of the store. false
    storeName The store name. undefined
    persistentStorage Set the persistent storage local or session undefined
    comparator A function used to compare the previous and current state for equality. a === b

    Testing

    ng-simple-state is simple to test. Eg.:

    import { TestBed } from '@angular/core/testing';
    import { provideNgSimpleState } from 'ng-simple-state';
    import { CounterStore } from './counter-store';
    
    describe('CounterStore', () => {
    
      let counterStore: CounterStore;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          providers: [
            provideNgSimpleState({
              enableDevTool: false
            }),
            CounterStore
          ]
        });
    
        counterStore = TestBed.inject(CounterStore);
      });
    
      it('initialState', () => {
        expect(counterStore.getCurrentState()).toEqual({ count: 0 });
      });
    
      it('increment', () => {
        counterStore.increment();
        expect(counterStore.getCurrentState()).toEqual({ count: 1 });
      });
    
      it('decrement', () => {
        counterStore.decrement();
        expect(counterStore.getCurrentState()).toEqual({ count: -1 });
      });
    
      it('selectCount', (done) => {
        counterStore.selectCount().subscribe(value => {
          expect(value).toBe(0);
          done();
        });
      });
    
    });

    Example: array store

    This is an example for a todo list store in a src/app/todo-store.ts file.

    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseRxjsStore } from 'ng-simple-state';
    import { Observable } from 'rxjs';
    
    export interface Todo {
      id: number;
      name: string;
      completed: boolean;
    }
    
    export type TodoState = Array<Todo>;
    
    @Injectable()
    export class TodoStore extends NgSimpleStateBaseRxjsStore<TodoState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'TodoStore'
        };
      }
    
      initialState(): TodoState {
        return [];
      }
    
      add(todo: Omit<Todo, 'id'>): void {
        this.setState(state =>  [...state, {...todo, id: Date.now()}]);
      }
    
      delete(id: number): void {
        this.setState(state => state.filter(item => item.id !== id) );
      }
    
      setComplete(id: number, completed: boolean = true): void {
        this.setState(state => state.map(item => item.id === id ? {...item, completed} : item) );
      }
    }

    usage:

    import { Component, inject } from '@angular/core';
    import { Observable } from 'rxjs';
    import { Todo, TodoStore } from './todo-store';
    
    @Component({
      selector: 'app-root',
      template: `
        <input #newTodo> <button (click)="todoStore.add({name: newTodo.value, completed: false})">Add todo</button>
        <ol>
          @for(todo of todoList$ | async; track todo.id) {
            <li>
                @if(todo.completed) {
                  βœ…
                } 
                {{ todo.name }} 
                <button (click)="todoStore.setComplete(todo.id, !todo.completed)">Mark as {{ todo.completed ? 'Not completed' : 'Completed' }}</button> 
                <button (click)="todoStore.delete(todo.id)">Delete</button>
            </li>
          }
        </ol>
      `,
      providers: [TodoStore]
    })
    export class AppComponent {
      public todoStore = inject(TodoStore);
      public todoList$: Observable<Todo[]> = this.todoStore.selectState();
    }

    NgSimpleStateBaseRxjsStore API

    @Injectable()
    @Directive()
    export abstract class NgSimpleStateBaseRxjsStore<S extends object | Array<any>> implements OnDestroy {
    
        /**
         * Return the observable of the state
         * @returns Observable of the state
         */
        public get state(): BehaviorSubject<S>;
    
        /**
         * When you override this method, you have to call the `super.ngOnDestroy()` method in your `ngOnDestroy()` method.
         */
        ngOnDestroy(): void;
    
        /**
         * Reset store to first loaded store state:
         *  - the last saved state
         *  - otherwise the initial state provided from `initialState()` method.
         */
        resetState(): boolean;
    
        /**
         * Restart the store to initial state provided from `initialState()` method
         */
        restartState(): boolean;
    
        /**
         * Override this method for set a specific config for the store
         * @returns NgSimpleStateStoreConfig
         */
        storeConfig(): NgSimpleStateStoreConfig<S>;
    
        /**
         * Set into the store the initial state
         * @returns The state object
         */
        initialState(): S;
    
        /**
         * Select a store state
         * @param selectFn State selector (if not provided return full state)
         * @param comparator A function used to compare the previous and current state for equality. Defaults to a `===` check.
         * @returns Observable of the selected state
         */
        selectState<K>(selectFn?: (state: Readonly<S>) => K, comparator?: (previous: K, current: K) => boolean): Observable<K>;
    
        /**
         * Return the current store state (snapshot)
         * @returns The current state
         */
        getCurrentState(): Readonly<S>;
    
        /**
         * Return the first loaded store state:
         * the last saved state
         * otherwise the initial state provided from `initialState()` method.
         * @returns The first state
         */
        getFirstState(): Readonly<S> | null;
    
        /**
         * Set a new state
         * @param newState New state
         * @param actionName The action label into Redux DevTools (default is parent function name)
         * @returns True if the state is changed
         */
        setState(newState: Partial<S>, actionName?: string): boolean;
        /**
         * Set a new state
         * @param selectFn State reducer
         * @param actionName The action label into Redux DevTools (default is parent function name)
         * @returns True if the state is changed
         */
        setState(stateFn: NgSimpleStateSetState<S>, actionName?: string): boolean;
    }

    Signal Store

    This is an example for a counter store in a src/app/counter-store.ts file. Obviously, you can create every store you want with every complexity you need.

    1. Define your state interface, eg.:
    export interface CounterState {
        count: number;
    }
    1. Define your store service by extending NgSimpleStateBaseSignalStore, eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseSignalStore } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseSignalStore<CounterState> {
     
    }
    1. Implement initialState() and storeConfig() methods and provide the initial state of the store, eg.:
    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseSignalStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseSignalStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
      
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
    }
    1. Implement one or more selectors of the partial state you want, in this example selectCount() eg.:
    import { Injectable, Signal } from '@angular/core';
    import { NgSimpleStateBaseSignalStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
     
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseSignalStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
      
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
      selectCount(): Signal<number> {
        return this.selectState(state => state.count);
      }
    }
    1. Implement one or more actions for change the store state, in this example increment() and decrement() eg.:
    import { Injectable, Signal } from '@angular/core';
    import { NgSimpleStateBaseSignalStore, NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    export interface CounterState {
      count: number;
    }
    
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseSignalStore<CounterState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'CounterStore'
        };
      }
    
      initialState(): CounterState {
        return {
          count: 0
        };
      }
    
      selectCount(): Signal<number> {
        return this.selectState(state => state.count);
      }
    
      increment(increment: number = 1): void {
        this.setState(state => ({ count: state.count + increment }));
      }
    
      decrement(decrement: number = 1): void {
        this.setState(state => ({ count: state.count - decrement }));
      }
    }

    Step 3: Inject your store into the providers, eg.:

    import { Component } from '@angular/core';
    import { CounterStore } from './counter-store';
    
    @Component({
      selector: 'app-root',
      imports: [CounterStore]
    })
    export class AppComponent {
    
    }

    Step 4: Use your store into the components, eg.:

    import { Component, Signal, inject } from '@angular/core';
    import { CounterStore } from './counter-store';
    
    @Component({
      selector: 'app-root',
      template: `
      <h1>Counter: {{ counterSig() }}</h1>
      <button (click)="counterStore.decrement()">Decrement</button>
      <button (click)="counterStore.resetState()">Reset</button>
      <button (click)="counterStore.increment()">Increment</button>
      `,
    })
    export class AppComponent {
      public counterStore = inject(CounterStore);
      public counterSig: Signal<number> = this.counterStore.selectCount();
    }

    That’s all!

    alt text

    Manage component state without service

    If you want manage just a component state without make a new service, your component can extend directly NgSimpleStateBaseSignalStore:

    import { Component, Signal } from '@angular/core';
    import { NgSimpleStateBaseSignalStore } from 'ng-simple-state';
    
    export interface CounterState {
        count: number;
    }
    
    @Component({
        selector: 'app-counter',
        template: `
            {{counterSig()}}
            <button (click)="increment()">+</button>
            <button (click)="decrement()">-</button>
        `
    })
    export class CounterComponent extends NgSimpleStateBaseSignalStore<CounterState> {
    
        public counterSig: Signal<number> = this.selectState(state => state.count);
    
        storeConfig(): NgSimpleStateStoreConfig<CounterState> {
          return {
            storeName: 'CounterComponent'
          };
        }
    
        initialState(): CounterState {
            return {
                count: 0
            };
        }
    
        increment(): void {
            this.setState(state => ({ count: state.count + 1 }));
        }
    
        decrement(): void {
            this.setState(state => ({ count: state.count - 1 }));
        }
    }

    Override global config

    If you need to override the global configuration provided by provideNgSimpleState() you can implement storeConfig() and return a specific configuration for the single store, eg.:

    import { Injectable } from '@angular/core';
    import { NgSimpleStateStoreConfig } from 'ng-simple-state';
    
    
    @Injectable()
    export class CounterStore extends NgSimpleStateBaseSignalStore<CounterState> {
    
      override storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          persistentStorage: 'session', // persistentStorage can be 'session' or 'local' (default is localStorage)
          storeName: 'CounterStore2', // set a specific name for this store (must be be unique)
        }
      }
    }

    The options are defined by NgSimpleStateStoreConfig interface:

    Option Description Default
    enableDevTool if true enable Redux DevTools browser extension for inspect the state of the store. false
    storeName The store name. undefined
    persistentStorage Set the persistent storage local or session undefined
    comparator A function used to compare the previous and current state for equality. a === b

    Testing

    ng-simple-state is simple to test. Eg.:

    import { TestBed } from '@angular/core/testing';
    import { provideNgSimpleState } from 'ng-simple-state';
    import { CounterStore } from './counter-store';
    
    describe('CounterStore', () => {
    
      let counterStore: CounterStore;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          providers: [
            provideNgSimpleState({
              enableDevTool: false
            }),
            CounterStore
          ]
        });
    
        counterStore = TestBed.inject(CounterStore);
      });
    
      it('initialState', () => {
        expect(counterStore.getCurrentState()).toEqual({ count: 0 });
      });
    
      it('increment', () => {
        counterStore.increment();
        expect(counterStore.getCurrentState()).toEqual({ count: 1 });
      });
    
      it('decrement', () => {
        counterStore.decrement();
        expect(counterStore.getCurrentState()).toEqual({ count: -1 });
      });
    
      it('selectCount', () => {
        const valueSig = counterStore.selectCount();
        expect(valueSig()).toBe(0);
      });
    
    });

    Example: array store

    This is an example for a todo list store in a src/app/todo-store.ts file.

    import { Injectable } from '@angular/core';
    import { NgSimpleStateBaseSignalStore } from 'ng-simple-state';
    
    export interface Todo {
      id: number;
      name: string;
      completed: boolean;
    }
    
    export type TodoState = Array<Todo>;
    
    @Injectable()
    export class TodoStore extends NgSimpleStateBaseSignalStore<TodoState> {
    
      storeConfig(): NgSimpleStateStoreConfig<CounterState> {
        return {
          storeName: 'TodoStore'
        };
      }
    
      initialState(): TodoState {
        return [];
      }
    
      add(todo: Omit<Todo, 'id'>): void {
        this.setState(state =>  [...state, {...todo, id: Date.now()}]);
      }
    
      delete(id: number): void {
        this.setState(state => state.filter(item => item.id !== id) );
      }
    
      setComplete(id: number, completed: boolean = true): void {
        this.setState(state => state.map(item => item.id === id ? {...item, completed} : item) );
      }
    }

    usage:

    import { Component, Signal, inject } from '@angular/core';
    import { Todo, TodoStore } from './todo-store';
    
    @Component({
      selector: 'app-root',
      template: `
        <input #newTodo> <button (click)="todoStore.add({name: newTodo.value, completed: false})">Add todo</button>
        <ol>
          @for(todo of todoListSig(); track todo.id) {
            <li>
                @if(todo.completed) {
                  βœ…
                }
                {{ todo.name }} 
                <button (click)="todoStore.setComplete(todo.id, !todo.completed)">Mark as {{ todo.completed ? 'Not completed' : 'Completed' }}</button> 
                <button (click)="todoStore.delete(todo.id)">Delete</button>
            </li>
          }
        </ol>
      `,
      providers: [TodoStore]
    })
    export class AppComponent {
      public todoStore = inject(TodoStore);
      public todoListSig: Signal<Todo[]> = this.todoStore.selectState();
    }

    NgSimpleStateBaseSignalStore API

    @Injectable()
    @Directive()
    export abstract class NgSimpleStateBaseSignalStore<S extends object | Array<any>> implements OnDestroy {
    
        /**
         * Return the Signal of the state
         * @returns Signal of the state
         */
        public get state(): Signal<S>;
    
        /**
         * When you override this method, you have to call the `super.ngOnDestroy()` method in your `ngOnDestroy()` method.
         */
        ngOnDestroy(): void;
    
        /**
         * Reset store to first loaded store state:
         *  - the last saved state
         *  - otherwise the initial state provided from `initialState()` method.
         */
        resetState(): boolean;
    
        /**
         * Restart the store to initial state provided from `initialState()` method
         */
        restartState(): boolean;
    
        /**
         * Override this method for set a specific config for the store
         * @returns NgSimpleStateStoreConfig
         */
        storeConfig(): NgSimpleStateStoreConfig<S>;
    
        /**
         * Set into the store the initial state
         * @returns The state object
         */
        initialState(): S;
    
        /**
         * Select a store state
         * @param selectFn State selector (if not provided return full state)
         * @param comparator A function used to compare the previous and current state for equality. Defaults to a `===` check.
         * @returns Signal of the selected state
         */
        selectState<K>(selectFn?: (state: Readonly<S>) => K, comparator?: (previous: K, current: K) => boolean): Signal<K>;
    
        /**
         * Return the current store state (snapshot)
         * @returns The current state
         */
        getCurrentState(): Readonly<S>;
    
        /**
         * Return the first loaded store state:
         * the last saved state
         * otherwise the initial state provided from `initialState()` method.
         * @returns The first state
         */
        getFirstState(): Readonly<S> | null;
    
        /**
         * Set a new state
         * @param newState New state
         * @param actionName The action label into Redux DevTools (default is parent function name)
         * @returns True if the state is changed
         */
        setState(newState: Partial<S>, actionName?: string): boolean;
        /**
         * Set a new state
         * @param selectFn State reducer
         * @param actionName The action label into Redux DevTools (default is parent function name)
         * @returns True if the state is changed
         */
        setState(stateFn: NgSimpleStateSetState<S>, actionName?: string): boolean; 
    }

    Alternatives

    Aren’t you satisfied? there are some valid alternatives:

    Support

    This is an open-source project. Star this repository, if you like it, or even donate. Thank you so much!

    My other libraries

    I have published some other Angular libraries, take a look:

    Visit original content creator repository https://github.com/nigrosimone/ng-simple-state
  • ReduxToolkit

    Getting Started with Create React App

    This project was bootstrapped with Create React App.

    Available Scripts

    In the project directory, you can run:

    npm start

    Runs the app in the development mode.
    Open http://localhost:3000 to view it in your browser.

    The page will reload when you make changes.
    You may also see any lint errors in the console.

    npm test

    Launches the test runner in the interactive watch mode.
    See the section about running tests for more information.

    npm run build

    Builds the app for production to the build folder.
    It correctly bundles React in production mode and optimizes the build for the best performance.

    The build is minified and the filenames include the hashes.
    Your app is ready to be deployed!

    See the section about deployment for more information.

    npm run eject

    Note: this is a one-way operation. Once you eject, you can’t go back!

    If you aren’t satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single build dependency from your project.

    Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.

    You don’t have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.

    Learn More

    You can learn more in the Create React App documentation.

    To learn React, check out the React documentation.

    Code Splitting

    This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting

    Analyzing the Bundle Size

    This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size

    Making a Progressive Web App

    This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app

    Advanced Configuration

    This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration

    Deployment

    This section has moved here: https://facebook.github.io/create-react-app/docs/deployment

    npm run build fails to minify

    This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

    Visit original content creator repository
    https://github.com/alphacodedev99/ReduxToolkit

  • TrashDash

    TrashDash

    I won the 2nd place hack at StartHacks 2017!!!! πŸ˜€

    Problem

    Every year in North America, the average consumer throws out between 95-115 kg of waste in just food products alone. How many drinks can we buy at Phils if we each save that money instead? Too many. In addition, as students, we also purchase many products that we use for one year then throw out such as tables, microwaves, and laptops.

    Vision

    The problem is with purchasing food or products that we think we will eat or use, but end up throwing out. We assume that it won’t happen next week/month/term/year. TrashDash uses your trash data to persuade you otherwise. TrashDash aims to minimize the amount of trash that you produce: both before AND after purchase.
    TrashDash seeks to bring Fitbit-level user tracking and gamification to the problem of waste.

    How it’s done

    Every time you dispose a piece of garbage, you take a picture of it and send it to the TrashDash Facebook Messenger Bot. The bot recognizes what the product is, and estimates the value of the product.

    Firstly, the bot aims to use this information change users purchase habits. At a grocery store, the bot tells you the food product categories with the most waste. Maybe you would buy less of those categories so you throw out less. Maybe you ignore those aisles completely. Looking to buy oranges but you threw out a bunch last week? TrashDash tells you to put them down.

    The other reason for this problem comes us impulsively purchase products we’ll never use. TrashDash aims to reduce the amount of trash by reusing whenever possible. For high value products (such as phones or laptops), the bot will help you to post it to Kijiji. Just specify the title, description, and price and wait for the responses to roll in.

    Both these solutions are held together with gamification. TrashDash also shows you the amount of trash that your friends throw out as well on your dashboard. This aims to persuade users to lower the trash they throw out relative to their friends and make the bot more addicting.

    Technology Stack

    Flask is used on the backend. Leveraging Clarifai API, Google Maps (Places) API, Facebook Messenger Bots API, and Facebook Graph API. Kijiji Posting accomplished with the KijijiRepostHeadless OSS project. The project also makes use of some natural language processing with TextBlob. Front end made in VueJS and vue-chart for max prettiness.

    Future Goals

    A lot of the potential of this bot relies on changing user habits. So, a natural next step is to analyze real use to identify ways to improve UX. Which actions are taking too long or too many clicks to complete? It’s hard work to change habits, making the process easier helps.

    Secondly, integration into a AR headset would be a doable and useful next step. Having a dashboard on the corner of your eye would be really useful for changing use purchasing habits. As well, being able to take pictures of your trash without your phone is really convenient.

    Finally, I would like to integrate TrashDash with Nest Cameras. Cameras would allow TrashDash to monitor the products (or foods) that are and are not used frequently. This would help make even better recommendations on what to and not to buy when the user is at the store. This would also remove the requirement of taking pictures of your trash.

    Visit original content creator repository
    https://github.com/ArthurG/TrashDash

  • mlvwmrc-openbsd-apm

    OpenBSD APM additions for MLVWM

    by Morgan Aldridge morgant@makkintosshu.com

    OVERVIEW

    A set of OpenBSD APM (Advanced Power Management) additions for MLVWM (specifically mlvwmrc), incl. MenuExtras which:

    • Provide a menu which allows the selection of CPU performance mode (manual or automatic), plus sleep/suspend/hybernate
    • Display statistics incl.: CPU speed, temperature, battery percentage, etc.

    This is loosely based on an OpenBSD-specific implementation of termbar.

    PREREQUISITES

    INSTALLATION

    The following will install the apmstats utility in ~/bin and the appropriate MenuExtras in ~/.mlvwm/MenuExtras:

    make install
    

    USAGE

    Edit your ~/.mlvwm/MenuBar file to insert any of the following MenuExtras you’d like to use:

    Read .mlvwm/MenuExtras/apm
    Read .mlvwm/MenuExtras/apmstats-cpu
    Read .mlvwm/MenuExtras/apmstats-cpustep
    Read .mlvwm/MenuExtras/apmstats-cpuload
    Read .mlvwm/MenuExtras/apmstats-cputemp
    Read .mlvwm/MenuExtras/apmstats-battery
    

    Then restart mlvwm.

    Note: While I suggest using SolΓ¨ne Rapenne’s obsdfreqd for customizing CPU performance, using apmd‘s automatic mode should not be used at the same time. The “apm” MenuExtra included in this project does not include obsdfreqd support, though the other MenuExtras will continue to show accurate CPU frequency/temperature, battery level, etc.

    LICENSE

    Released under the MIT License.

    Some icons by Yusuke Kamiyamane. Licensed under a Creative Commons Attribution 3.0 License.

    Visit original content creator repository
    https://github.com/morgant/mlvwmrc-openbsd-apm

  • Waste_Watcher_Adafruit_IO

    Waste Watcher Adafruit IO

    All Contributors

    Waste Watcher is an IoT based sensor module to help keep track of waste data metrics with real-time bin fullness data and waste images. This is intended to help quantify and promote actionable solutions to reduce waste. This project is a continuation of waste_watcher.

    Features

    • πŸ—‘ Collect waste bin fullness telemetry
    • πŸ“· Capture image of inside the waste bin
    • πŸ”§ Fully customizable (adjust data collection rate, tweak case design for your needs, change camera settings, etc.)

    Why? Β―\(ツ)/Β―

    Simply put, we generate way too much waste here in the United States 🀧. According to the EPA, 267.8 million tons of trash were generated. And even though the US represents 4% of the global population we generate 12% of global municipal solid waste, right behind India and China ( Verisk Maplecroft, 2019). The trash itself also presents other harmful environmental impacts. US landfills are the 3rd largest source of human-related methane emissions (EPA). Waste also affects 43% of marine mammals (EPA 2017).

    Waste monitoring systems already exist in commercial settings, ZotBins expands waste monitoring to the individual and community level. Some of these existing systems include Bigbelly, Compology, and Enevo. ZotBins is completely opensource. Built for people, by people.

    Adafruit IO

    This repo, in particular, uses Adafruit IO, an off-the-shelf IoT platform. For now, Adafruit IO is a great addition to this project and helps in the following ways:

    • πŸ€— Helps makes the project more user-friendly (no need to setup your own server, ready to use APIs, Dashboard feature)
    • πŸ†“ Free to use F O R E V E R β•°(Β°β–½Β°)β•― (according to their website)
    • 🀝 Great community on Discord willing to help people out
    • πŸ”’ Data is never sold

    Getting Started

    Follow the Build Guide πŸ› .

    Actionable Ideas for Your Waste Data

    Besides just collecting data, here are some ideas that you can implement. Again the goal of this project is to help quantify and promote actionable solutions to reduce waste.

    • 🎯 Create a goal to reduce the rate at which you fill up a bin
    • πŸ’» Use the Adafruit IO Integrations (Zapier, IFTTT)
      • Send a summary of weekly fullness rate
      • Send alerts when bin is full
    • πŸ€– Use machine learning to identify what items are thrown always
      • identify frequently thrown away items then try to reduce that waste (eg. plastic straws are identified frequently in the waste stream, so we can ban the straw or give sustainable alternatives)
      • identify which waste items are incorrectly thrown away

    Contributors ✨

    Thanks goes to these wonderful people (emoji key):


    Primal Pappachan

    πŸ‘€

    Patrick Nguyen

    πŸ’» πŸ‘€

    This project follows the all-contributors specification. Contributions of any kind welcome!

    Credits/Acknowledgements

    1. ESP32Cam-MQTT by abish7643 – Great reference in how to send images through MQTT
    2. ESP32-CAM Projects by Random Nerd Tutorials – Quality background information on getting started with the ESP32-CAM

    How this Project Started

    This project started at UCI in 2017 with a group of 4 undergraduates for a hackathon hosted by UCI TIPPERS. The aim of the project was to help create a tool to help with UCI’s zero waste initiatives. Since then, the project is being shared to the open-source community through ZBCE and the ZotBins UCI undergraduate team continues with new members every year.

    Other Links

    1. License
    2. Contributing
    3. Code of Conduct
    Visit original content creator repository https://github.com/zotbins/Waste_Watcher_Adafruit_IO