diff --git a/main.py b/main.py index 9767bbb..a644d75 100644 --- a/main.py +++ b/main.py @@ -35,6 +35,9 @@ async def _main(self): async def get_settings(self): results = settings.get_settings() + if results.get("chargeLimitEnabled", False): + legion_space.set_charge_limit(True) + try: results['pluginVersionNum'] = f'{decky_plugin.DECKY_PLUGIN_VERSION}' @@ -141,6 +144,13 @@ async def set_power_led(self, enabled): legion_space.set_power_light(enabled) + async def set_charge_limit(self, enabled): + try: + settings.set_setting('chargeLimitEnabled', enabled) + + legion_space.set_charge_limit(enabled) + except Exception as e: + decky_plugin.logger.error(f'error while setting charge limit {e}') async def remap_button(self, button: str, action: str): decky_plugin.logger.info(f"remap_button {button} {action}") diff --git a/py_modules/legion_space.py b/py_modules/legion_space.py index e3ee3bc..17fd7fa 100644 --- a/py_modules/legion_space.py +++ b/py_modules/legion_space.py @@ -139,8 +139,13 @@ def get_charge_limit(): # off # echo '\_SB.GZFD.WMAE 0 0x12 {0x01, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00}' | sudo tee /proc/acpi/call # 80% charge limit -def set_charging_limit(enabled: bool): - if enabled: +def set_charge_limit(enabled: bool): + + current_charge_limit = get_charge_limit() + + # decky_plugin.logger.info(f'charge limit {current_charge_limit} {current_charge_limit == 0}') + + if enabled and current_charge_limit == 0: return call( r"\_SB.GZFD.WMAE", [ @@ -160,7 +165,7 @@ def set_charging_limit(enabled: bool): ), ], ) - else: + elif not enabled and current_charge_limit == 1: return call( r"\_SB.GZFD.WMAE", [ @@ -180,6 +185,8 @@ def set_charging_limit(enabled: bool): ), ], ) + # no changes required + return True def call(method: str, args: Sequence[bytes | int], risky: bool = True): cmd = method diff --git a/src/backend/utils.tsx b/src/backend/utils.tsx index 6baab26..1df1da9 100644 --- a/src/backend/utils.tsx +++ b/src/backend/utils.tsx @@ -7,7 +7,8 @@ export enum ServerAPIMethods { REMAP_BUTTON = 'remap_button', LOG_INFO = 'log_info', GET_SETTINGS = 'get_settings', - SET_POWER_LED = 'set_power_led' + SET_POWER_LED = 'set_power_led', + SET_CHARGE_LIMIT = 'set_charge_limit' } const createRgbOn = @@ -46,6 +47,13 @@ const createSetPowerLed = }); }; +const createSetChargeLimit = + (serverAPI: ServerAPI) => async (enabled: boolean) => { + await serverAPI.callPluginMethod(ServerAPIMethods.SET_CHARGE_LIMIT, { + enabled + }); + }; + const createGetSettings = (serverAPI: ServerAPI) => async () => { return await serverAPI.callPluginMethod(ServerAPIMethods.GET_SETTINGS, {}); }; @@ -73,7 +81,8 @@ export const createServerApiHelpers = (serverAPI: ServerAPI) => { remapButton: createRemapButtons(serverAPI), logInfo: createLogInfo(serverAPI), getSettings: createGetSettings(serverAPI), - setPowerLed: createSetPowerLed(serverAPI) + setPowerLed: createSetPowerLed(serverAPI), + setChargeLimit: createSetChargeLimit(serverAPI) }; }; diff --git a/src/hooks/ui.tsx b/src/hooks/ui.tsx new file mode 100644 index 0000000..d84a2a5 --- /dev/null +++ b/src/hooks/ui.tsx @@ -0,0 +1,13 @@ +import { useDispatch, useSelector } from 'react-redux'; +import { selectChargeLimitEnabled, uiSlice } from '../redux-modules/uiSlice'; + +export const useChargeLimitEnabled = () => { + const chargeLimitEnabled = useSelector(selectChargeLimitEnabled); + const dispatch = useDispatch(); + + const setChargeLimit = (enabled: boolean) => { + return dispatch(uiSlice.actions.setChargeLimit(enabled)); + }; + + return { chargeLimitEnabled, setChargeLimit }; +}; diff --git a/src/index.tsx b/src/index.tsx index 826abc8..3bedea4 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,11 @@ -import { definePlugin, ServerAPI, staticClasses } from 'decky-frontend-lib'; +import { + definePlugin, + PanelSection, + PanelSectionRow, + ServerAPI, + staticClasses, + ToggleField +} from 'decky-frontend-lib'; import { memo, VFC } from 'react'; import RemapButtons from './components/controller/RemapButtons'; @@ -13,14 +20,25 @@ import logo from '../assets/Icon.png'; import FanPanel from './components/fan/FanPanel'; import ErrorBoundary from './components/ErrorBoundary'; import OtaUpdates from './components/OtaUpdates'; +import { useChargeLimitEnabled } from './hooks/ui'; const Content: VFC<{ serverAPI?: ServerAPI }> = memo(() => { const loading = useSelector(getInitialLoading); + const { chargeLimitEnabled, setChargeLimit } = useChargeLimitEnabled(); if (loading) { return null; } return ( <> + + + + + diff --git a/src/redux-modules/store.tsx b/src/redux-modules/store.tsx index ce96ad4..da9d8ea 100644 --- a/src/redux-modules/store.tsx +++ b/src/redux-modules/store.tsx @@ -1,6 +1,6 @@ import { configureStore } from '@reduxjs/toolkit'; import { rgbSlice, saveRgbSettingsMiddleware } from './rgbSlice'; -import { uiSlice } from './uiSlice'; +import { uiSlice, uiSliceMiddleware } from './uiSlice'; import { controllerSlice, saveControllerSettingsMiddleware @@ -17,6 +17,7 @@ export const store = configureStore({ }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat([ + uiSliceMiddleware, saveRgbSettingsMiddleware, saveControllerSettingsMiddleware, saveFanSettingsMiddleware diff --git a/src/redux-modules/uiSlice.tsx b/src/redux-modules/uiSlice.tsx index a61befe..dbffb65 100644 --- a/src/redux-modules/uiSlice.tsx +++ b/src/redux-modules/uiSlice.tsx @@ -2,13 +2,19 @@ import { createSlice } from '@reduxjs/toolkit'; import type { PayloadAction } from '@reduxjs/toolkit'; import { setCurrentGameId, setInitialState } from './extraActions'; import { RootState } from './store'; -import { extractDisplayName, logInfo } from '../backend/utils'; +import { + createServerApiHelpers, + extractDisplayName, + getServerApi, + logInfo +} from '../backend/utils'; // import type { RootState } from './store'; type UiStateType = { initialLoading: boolean; currentGameId: undefined | string; currentDisplayName: undefined | string; + chargeLimitEnabled?: boolean; pluginVersionNum?: string; }; @@ -27,6 +33,9 @@ export const uiSlice = createSlice({ reducers: { setInitialLoading: (state, action: PayloadAction) => { state.initialLoading = action.payload; + }, + setChargeLimit(state, action: PayloadAction) { + state.chargeLimitEnabled = action.payload; } }, extraReducers: (builder) => { @@ -35,6 +44,9 @@ export const uiSlice = createSlice({ if (action.payload?.pluginVersionNum) { state.pluginVersionNum = `${action.payload.pluginVersionNum}`; } + if (action.payload?.chargeLimitEnabled) { + state.chargeLimitEnabled = Boolean(action.payload?.chargeLimitEnabled); + } }); builder.addCase(setCurrentGameId, (state, action) => { if (action?.payload) { @@ -55,3 +67,22 @@ export const selectCurrentGameId = (state: RootState) => export const selectCurrentGameDisplayName = (state: RootState) => state.ui?.currentDisplayName || 'default'; + +export const selectChargeLimitEnabled = (state: RootState) => + Boolean(state.ui?.chargeLimitEnabled); + +export const uiSliceMiddleware = + (store: any) => (next: any) => (action: any) => { + const { type } = action; + const serverApi = getServerApi(); + + const result = next(action); + + if (type === uiSlice.actions.setChargeLimit.type && serverApi) { + const { setChargeLimit } = createServerApiHelpers(serverApi); + + setChargeLimit(action.payload); + } + + return result; + };