Compare commits

...

191 Commits

Author SHA1 Message Date
74362a0dad back to 2.5 gig for now 2026-09-16 20:49:05 +02:00
f46e7df5f3 it was annoyign 2026-09-06 14:55:29 +02:00
ddf0868c9e needs a whole lotta contrast for some reaosn now 2026-09-06 14:55:23 +02:00
159406d327 man ts bggy 2026-09-04 00:54:10 +02:00
f8046918c6 pump switch point tweak 2026-09-02 17:08:12 +02:00
3ecdc57309 distro name 2026-09-01 21:22:44 +02:00
739c62effa tweakin 2026-08-24 23:50:09 +02:00
a53fc1b33f tweakin 2026-08-24 23:43:53 +02:00
34173674d8 v stupid bug 2026-08-24 23:43:36 +02:00
7b37b2052d oh and dont spam debug 2026-08-24 22:47:26 +02:00
7043284dcb uknown also means bad read 2026-08-24 22:46:14 +02:00
c748e60915 ts was dumb. doesnt seem to set fan curve though? 2026-08-24 22:43:15 +02:00
e6170fc9cc ? why was i casting this to uint8_t? 2026-08-21 00:39:18 +02:00
f74af3122c didnt update readme in a bit 2026-08-14 18:35:29 +02:00
70eaad2f5a mesonless 2026-08-14 18:10:25 +02:00
ad6e378340 we dab on meson 2026-08-14 18:08:52 +02:00
81cfc2ce0d hyperoptimized file reading 2026-08-14 18:00:02 +02:00
901d5b53d9 we love errors now 2026-08-14 17:11:57 +02:00
1103434857 dont log debug 2026-08-14 17:11:46 +02:00
8bac9bde42 buitching 2026-08-14 16:31:20 +02:00
78a77164fb experiment 2026-08-14 01:50:58 +02:00
1f70f2ead2 refactor enum, it was dumb 2026-08-14 01:05:17 +02:00
cd06d047d5 aio sanity checks 2026-08-14 01:03:46 +02:00
31cd2ef211 make bufs static 2026-08-13 18:15:01 +02:00
df6f9f3986 let it ride 2026-08-13 17:59:00 +02:00
046cff01f8 errorchecking for fans 2026-08-13 17:58:53 +02:00
cf203edc32 likes when i do this 2026-08-13 17:54:51 +02:00
2d3c115703 puno counter 2026-08-13 17:54:42 +02:00
ddb9ea7173 oops bug 2026-08-13 17:54:26 +02:00
e4e7c4bbc2 add makefile, fix annoying errors 2026-08-13 17:39:36 +02:00
73a9b4303d less restarted way to do contaiers 2026-08-01 15:52:33 +02:00
8c2296ca1f even higher temp? 2026-07-23 19:25:48 +02:00
b2af4630b9 mah 2026-07-23 19:25:41 +02:00
6a24d54a6c nah aio logic sucks 2026-07-23 19:23:01 +02:00
a89e227d95 atp ye sure 2026-07-22 00:06:50 +02:00
e816a7d8ee fixed water temp bug? 2026-07-22 00:01:22 +02:00
c31c3d6e67 oops 2026-07-22 00:00:58 +02:00
7b74e69759 better version handling 2026-07-21 23:59:31 +02:00
c693e892d0 some wack overflow bug? 2026-07-21 22:48:46 +02:00
c2f19b83f4 so it donesnt complain 2026-07-21 20:59:54 +02:00
7caef9c744 tell it what ver it is 2026-07-21 20:57:39 +02:00
9419230747 sane defaults on exit 2026-07-21 20:45:02 +02:00
116eb7201f drop debug info 2026-07-21 20:42:06 +02:00
27b083f388 add counter to the scr 2026-07-21 20:41:51 +02:00
42620b3577 huge aio logic rewrite 2026-07-21 20:38:18 +02:00
f4503370fd huge aio logic rewrite 2026-07-21 20:38:08 +02:00
de500dc5e5 this should fix the read bugs 2026-07-21 14:36:35 +02:00
3f0a96ef7f warns 2026-07-20 20:45:20 +02:00
4ecce5c4d0 sanityy check 2026-07-20 20:45:08 +02:00
12135f0a33 dont debug 2026-07-20 20:44:59 +02:00
7bb7281df8 sanity 2026-07-20 20:44:38 +02:00
c2d53ad334 ts was buggy 2026-07-20 20:44:31 +02:00
988a44b8f4 refactor the vars 2026-07-10 12:56:28 +02:00
7eaa63a1aa we alredy set the fan curve in aio.c, lets set pump set points there too 2026-07-10 12:54:31 +02:00
816698f2a5 tweak fan curve 2026-07-10 12:52:58 +02:00
9a9ecc644c tweak aio temp points 2026-07-10 12:52:38 +02:00
92ce83ab4f aio now has extreme mode 2026-07-10 12:21:49 +02:00
b3397abe90 how did this get left like this 2026-07-10 12:21:25 +02:00
9afef7fbd8 log if we exit bc another instance is up 2026-07-10 12:21:13 +02:00
5a055fb03e ig we still free it? 2026-07-03 00:54:52 +02:00
5609ae5055 the nullptr update 2026-07-03 00:49:26 +02:00
88387704e1 possible bug, also consistency 2026-07-03 00:45:37 +02:00
6920f9f34f werent using date, reconsider how time/date works 2026-07-03 00:43:26 +02:00
6b035a0b81 why are we supressing deez 2026-07-02 15:31:18 +02:00
d883bdc6ff rename, remove padding 2026-07-02 15:31:09 +02:00
7f6ef12201 be more clever about custom chars 2026-07-02 15:30:59 +02:00
13e658087b maybe this will reopen properly? 2026-07-02 15:30:50 +02:00
7b89645114 whitespace 2026-07-02 15:29:57 +02:00
e270aa3ac5 we ignore padding issues 2026-07-02 15:29:49 +02:00
e439831fb0 caused a regression 2026-07-02 15:29:30 +02:00
4858feb4c7 make it more or less work with 'warning_level=everything' 2026-07-02 13:19:42 +02:00
1d2095261f potential bugs that warning=everything caught 2026-07-02 01:11:44 +02:00
a0bbc71cd8 finessed meson file 2026-07-02 01:11:23 +02:00
2f8d08a1f9 remove dead codepath, we dont need gz logs 2026-07-02 00:53:03 +02:00
3d869b5169 doesnt like ubuntu 24.04 anymore, damn 2026-07-01 21:22:16 +02:00
96a55076f6 Update README.md 2026-07-01 01:55:45 +02:00
29c14dc56f c23ify 2026-07-01 01:50:02 +02:00
86da8434bd how 2 work meson 2026-07-01 01:49:52 +02:00
782b57cbb5 meson/c23 for no real reason 2026-06-30 18:18:14 +02:00
ea05923bc7 dont need ts 2026-06-28 16:04:34 +02:00
735ad9cdff whitespae 2026-06-26 18:49:02 +02:00
9011767f76 Update README.md 2026-06-26 15:30:07 +02:00
43d7f0ed4a thinkinh 2026-06-24 21:07:30 +02:00
f7ed895f3b again slightly less dumb 2026-06-24 21:04:00 +02:00
42f702ca1b ig this is even less stupid 2026-06-24 20:35:03 +02:00
0352675e79 ig this is least stupid 2026-06-24 20:32:40 +02:00
1099bb6688 these functions are safer 2026-06-24 20:20:36 +02:00
b46f457afc bug? 2026-06-16 10:31:09 +02:00
2a75c9eb24 less hacky 2026-06-14 12:09:45 +02:00
4875fa4093 oh ye remove that 2026-06-14 11:56:50 +02:00
c4e80fbb69 fix char bug 2026-06-14 11:55:28 +02:00
804e477d50 optimize sending custom chars, one write operation now 2026-06-14 11:55:14 +02:00
8bea9be551 we might send more than 255 bytes at a time, hint hint 2026-06-14 11:54:50 +02:00
d4d12cc3c1 werent using this anyway 2026-06-14 11:13:27 +02:00
d44683a991 we were fuckin w the bios 2026-06-14 11:07:48 +02:00
0cd7520cf4 its okay to close the fd even if it isnt properly opened 2026-06-14 11:07:37 +02:00
3d40ebeb84 dont need to tell this each time 2026-06-14 11:07:13 +02:00
0bcc340d75 slower fan 2026-06-14 10:58:23 +02:00
2b6a72fcb2 adjust pump speed thresholds 2026-06-14 10:58:14 +02:00
2cac64c448 modemmanager was fucking me up 2026-06-14 10:57:59 +02:00
2bfef862ce this too ig 2026-06-07 18:05:31 +02:00
03c426195e silly off by 1 errors 2026-06-07 17:54:38 +02:00
85f87677d7 we need more packages actually 2026-06-07 17:51:29 +02:00
f74d9bf539 set fan curve functionality 2026-06-07 17:50:46 +02:00
f9272f8d1c logger 2026-06-07 17:50:23 +02:00
6d8b74ce3d dont actually fw colors its good 2026-06-07 17:49:56 +02:00
21d897886c lower timeout 2026-06-07 17:49:07 +02:00
4a80e795b2 1s interval 2026-06-07 17:41:54 +02:00
29d2385bd1 bro......... 2026-05-27 13:23:18 +00:00
07f391d8f2 oops 2026-05-25 15:55:28 +00:00
632abb467d readme update 2026-05-25 15:31:13 +00:00
e01748808a readme update 2026-05-25 15:30:17 +00:00
da8aec78a8 add more feature creep (set colors) 2026-05-25 15:28:26 +00:00
920600b294 neater temp setting 2026-05-25 13:41:24 +00:00
a28da52f8a refactor name 2026-05-25 13:22:37 +00:00
b46731a9e1 add buncha shit to aio, fix not working after reboot, autoajust pump speed 2026-05-25 13:21:52 +00:00
13e2c083cb enable aio again, ig it works 2026-05-25 13:21:22 +00:00
1db30f2eba ig systemd could restart it if it exits with 0 2026-05-25 11:19:16 +00:00
5e54828b65 fix service 2026-05-25 11:19:02 +00:00
b6ac658974 back to 10g 2026-05-24 23:07:48 +00:00
542bd6f36c refactor variable (static) 2026-05-24 12:51:47 +02:00
000b0bd543 comemnts mostly 2026-05-24 12:50:35 +02:00
4189148abc make macro more readable 2026-05-24 12:46:32 +02:00
d0faf1d84a make comments while u still remember 2026-05-24 12:46:20 +02:00
9de7379f45 macro? 2026-05-24 12:40:57 +02:00
5bfdda4b21 we dont even care about transferred, point ts to null, should work 2026-05-24 12:40:40 +02:00
952e984f65 make these shits static 2026-05-24 12:34:39 +02:00
e083dcc447 wasnt even using the ret value 2026-05-24 12:33:30 +02:00
5686dcdbf6 probably not even needed 2026-05-24 12:32:37 +02:00
4478df4f24 uhm it is used actually 2026-05-24 12:30:36 +02:00
b2e98f27f5 moved variable to inner scope 2026-05-24 12:30:19 +02:00
a6a1a58bcf flock implemented 2026-05-24 12:29:59 +02:00
6524ce346f idk man C version seems to work good 2026-05-24 12:29:11 +02:00
e9ca7d1691 we need libusb for aio 2026-05-24 12:28:14 +02:00
c3d1efbc01 flock mechanic added 2026-05-24 12:28:00 +02:00
f25a361bb0 flock idea 2026-05-23 21:06:40 +02:00
415310603e rest of aio shi 2026-05-23 18:59:39 +00:00
e7a42c92c9 not even using fan rn 2026-05-23 18:59:29 +00:00
9cee96a64c add aio 2026-05-23 18:09:22 +00:00
35aba48c48 make ts take less space 2026-05-23 18:09:14 +00:00
a247e8ecf6 add aio reading 2026-05-23 18:08:36 +00:00
4777d51107 sanity check for power 2026-05-23 18:08:29 +00:00
b17c02127e more contrast 2026-05-23 14:50:18 +00:00
39174cb5f2 we on an aio now, only monitor one fan 2026-05-23 14:49:49 +00:00
d903566362 issue 2026-05-23 14:46:12 +00:00
74bcbb6486 add gry monitoring, too wide tho, needs fix 2026-05-23 14:46:04 +00:00
8c2f95b2b6 Update README.md 2026-05-21 00:29:10 +02:00
54c178e3f5 bug 2026-05-12 22:14:38 +00:00
8c21c37b84 temp fix for the gpu thing 2026-05-11 17:41:20 +00:00
4650c178ff bugi 2026-05-11 17:31:58 +00:00
8ff87d2600 uwu 2026-05-11 12:14:13 +00:00
ffc1d55fde use the simplified shit 2026-05-11 12:13:04 +00:00
77f5494dd9 simplify shit 2026-05-11 12:12:43 +00:00
00656d4ba3 oops bug 2026-05-11 12:11:15 +00:00
07a8ecddf9 Update README.md 2026-05-11 13:27:17 +02:00
00b06ef6c1 Update README.md 2026-05-11 13:26:46 +02:00
cc4605f528 Update README.md 2026-05-11 13:22:59 +02:00
72587c72a8 Update README.md 2026-05-11 13:22:01 +02:00
1dd46c8761 Update README.md 2026-05-11 13:20:44 +02:00
3d00f10b77 pretty 2026-05-11 11:02:40 +00:00
0a19965657 wym hpp 2026-05-11 11:02:35 +00:00
aa57cdf651 consistency, optminization 2026-05-11 11:02:24 +00:00
574f59b8f5 make it take size after all 2026-05-11 11:01:37 +00:00
c26ccba813 formatting shit 2026-05-11 10:26:30 +00:00
5b8708ad0c ok 2026-05-11 10:26:23 +00:00
9b62096d5e formatting update 2026-05-11 10:26:15 +00:00
938b3af523 more cleanup 2026-05-11 11:20:16 +02:00
ac75332680 readme wasnt upsaded in ages 2026-05-11 11:17:44 +02:00
c1fd1f706e c99 now 2026-05-11 11:13:54 +02:00
c93ae21114 cleanup 2026-05-11 10:51:48 +02:00
c44365d78b sort includes 2026-05-11 10:45:51 +02:00
88c6db07c2 add and use pretty num 2026-05-11 10:45:42 +02:00
694b496282 fuck hass, its ugly and i dont like it and the binary is huge 2026-05-11 10:37:18 +02:00
169ca4b59b preliminary hass 2026-05-11 01:44:26 +02:00
f0dc384d37 implemented gpu, rest of the shit, mostly works except for hass 2026-05-11 01:44:12 +02:00
88764a8900 added gpu 2026-05-11 01:43:55 +02:00
9bd2357b96 progress 2026-05-10 21:17:36 +02:00
f28cd4c31a lcd bug, also removed cc 2026-05-10 19:19:19 +02:00
39193cdc9f works 2026-05-10 18:16:43 +02:00
ebd96aa8fb makes more senese like this 2026-05-10 18:16:37 +02:00
fd0a53f789 main 2026-05-10 00:48:32 +02:00
35e2e83862 network added 2026-05-10 00:48:27 +02:00
3833c9be6b cpu ready? 2026-05-10 00:48:17 +02:00
8e2d2e04de nah it was ass 2026-05-10 00:47:59 +02:00
4be515392d add ram and disk getters 2026-05-09 22:36:21 +02:00
110c012587 doing sth 2026-05-05 17:49:37 +02:00
6c8739ecd5 cleanup, cify 2026-05-04 23:37:58 +02:00
cab3c32c63 cooking continues 2026-05-04 23:04:43 +02:00
a82fdd3d33 whole 1st line done basically 2026-05-04 23:04:30 +02:00
a90a65ba96 ts dont run bri 2026-05-02 20:32:47 +02:00
96e14ed6ea we're getttng C to work
C deez nuts
2026-05-02 19:22:29 +02:00
49 changed files with 2497 additions and 2766 deletions

View File

@@ -1,4 +1,56 @@
--- BasedOnStyle: LLVM
BasedOnStyle: Google
IndentWidth: 4 # Indentation (Linux kernel style)
ColumnLimit: 0 UseTab: Always
TabWidth: 8
IndentWidth: 8
ContinuationIndentWidth: 8
# Keep parameters on one line as much as possible
BinPackParameters: true
BinPackArguments: true
# Allow long lines (important!)
ColumnLimit: 0 # or set to something large like 120
# Prevent breaking after return type / before function name
AlwaysBreakAfterReturnType: None
AlwaysBreakAfterDefinitionReturnType: None
# Keep function declarations/definitions on one line if possible
PenaltyBreakBeforeFirstCallParameter: 1000000
PenaltyReturnTypeOnItsOwnLine: 1000000
# Braces: K&R style everywhere, INCLUDING functions (your modification)
BreakBeforeBraces: Attach
# Control specific brace wrapping behavior
BraceWrapping:
AfterFunction: false # <-- key change (same line for functions)
AfterControlStatement: false
AfterStruct: false
AfterEnum: false
AfterUnion: false
AfterClass: false
BeforeElse: false
# Spaces
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
# Pointer alignment (kernel prefers this style)
PointerAlignment: Right
# Keep simple statements compact
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
# For-loop style similar to kernel
AllowShortBlocksOnASingleLine: Never
# Don't align weirdly
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
SortIncludes: CaseSensitive

10
.idea/.gitignore generated vendored
View File

@@ -1,10 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

View File

@@ -1,105 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<RiderCodeStyleSettings>
<option name="/Default/CodeStyle/CodeFormatting/CppClangFormat/EnableClangFormatSupport/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/EditorConfig/EnableClangFormatSupport/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_BINARY_EXPRESSIONS_CHAIN/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXPRESSION/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_FOR_STMT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTIPLE_DECLARATION/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_TERNARY/@EntryValue" value="ALIGN_ALL" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_CLASS_DEFINITION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue" value="2" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_CODE/@EntryValue" value="2" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_USER_LINEBREAKS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CASE_FROM_SWITCH/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_COMMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INT_ALIGN_EQ/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SIMPLE_BLOCK_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_ARGS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_PARAMS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_SEMICOLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_SEMICOLON/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_UNARY_OPERATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_ARRAY_ACCESS_BRACKETS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_METHOD_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPECIAL_ELSE_IF_TREATMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_BINARY_OPSIGN/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_TERNARY_OPSIGNS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TYPE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/OTHER_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CASE_BLOCK_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DECLARATION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DEFINITION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_WHILE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_ELSE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_CATCH_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_INDENTATION/@EntryValue" value="All" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_ARGUMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_PARAMETER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_ARGUMENT/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_PARAMETER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_DECLARATIONS/@EntryValue" value="0" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_ACCESS_SPECIFIERS_FROM_CLASS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CLASS_MEMBERS_FROM_ACCESS_SPECIFIERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/LINE_BREAK_AFTER_COLON_IN_MEMBER_INITIALIZER_LISTS/@EntryValue" value="ON_SINGLE_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/MEMBER_INITIALIZER_LIST_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_NAMESPACE_DEFINITIONS_ON_SAME_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_COLON/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBERS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_METHOD/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_NESTED_DECLARATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBERS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_METHOD/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_METHOD/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_METHOD/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BETWEEN_CLOSING_ANGLE_BRACKETS_IN_TEMPLATE_ARGS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_DECLARATION_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_BLOCKS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_RPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_RPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_ARGUMENTS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_PARAMETERS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BREAK_TEMPLATE_DECLARATION/@EntryValue" value="LINE_BREAK" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INVOCABLE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INITIALIZER_BRACES/@EntryValue" value="END_OF_LINE_NO_SPACE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_STYLE/@EntryValue" value="Space" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_SIZE/@EntryValue" value="4" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CONTINUOUS_LINE_INDENT/@EntryValue" value="Double" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TAB_WIDTH/@EntryValue" value="4" type="int" />
</RiderCodeStyleSettings>
<clangFormatSettings>
<option name="ENABLED" value="true" />
</clangFormatSettings>
</code_scheme>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -1,7 +0,0 @@
<component name="ProjectDictionaryState">
<dictionary name="project">
<words>
<w>drath</w>
</words>
</dictionary>
</component>

345
.idea/editor.xml generated
View File

@@ -1,345 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="BackendCodeEditorSettings">
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CDeclarationWithImplicitIntType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CommentTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConstevalIfIsAlwaysConstant/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractClassWithoutSpecifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractFinalClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractVirtualFunctionCallInCtor/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAccessSpecifierWithNoDeclarations/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAwaiterTypeIsNotClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBooleanIncrementExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatBadCode/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatLegacyCode/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatMixedArgs/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooFewArgs/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooManyArgs/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCVQualifierCanNotBeAppliedToReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassIsIncomplete/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeedsConstructorBecauseOfUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCompileTimeConstantCanBeReplacedWithBooleanConstant/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConceptNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConditionalExpressionCanBeSimplified/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConstParameterInDeclaration/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConstValueFunctionReturnType/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCoroutineCallResolveError/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAArrayIndexOutOfBounds/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantConditions/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantFunctionResult/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantParameter/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFADeletedPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAEndlessLoop/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAInfiniteRecursion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAInvalidatedMemory/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALocalValueEscapesFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALocalValueEscapesScope/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALoopConditionNotUpdated/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAMemoryLeak/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFANotInitializedField/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFANullDereference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFATimeOver/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreachableCode/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreachableFunctionCall/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreadVariable/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnusedValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationHidesLocal/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationHidesUncapturedLocal/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationSpecifierWithoutDeclarators/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorDisambiguatedAsFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorUsedBeforeInitialization/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultCaseNotHandledInSwitchStatement/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultInitializationWithNoUserConstructor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultIsUsedAsIdentifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultedSpecialMemberFunctionIsImplicitlyDeleted/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefinitionsOrder/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeletingVoidPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDependentTemplateWithoutTemplateKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDependentTypeWithoutTypenameKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedEntity/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedOverridenMethod/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedRegisterStorageClassSpecifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDereferenceOperatorLimitExceeded/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDiscardedPostfixOperatorResult/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenSyntaxError/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenUndocumentedParameter/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenUnresolvedReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEmptyDeclaration/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceCVQualifiersOrder/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceCVQualifiersPlacement/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceDoStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceForStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceFunctionDeclarationStyle/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceIfStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceNestedNamespacesStyle/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceOverridingDestructorStyle/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceOverridingFunctionStyle/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceTypeAliasCodeStyle/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceWhileStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEntityAssignedButNoRead/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEntityUsedOnlyInUnevaluatedContext/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnumeratorNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEqualOperandsInBinaryExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEvaluationFailure/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppExplicitSpecializationInNonNamespaceScope/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppExpressionWithoutSideEffects/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFinalFunctionInFinalClass/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFinalNonOverridingVirtualFunction/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppForLoopCanBeReplacedWithWhile/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppForwardEnumDeclarationWithoutUnderlyingType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionDoesntReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionIsNotImplemented/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionResultShouldBeUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionalStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHeaderHasBeenAlreadyIncluded/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHiddenFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHidingFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIdenticalOperandsInBinaryExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIfCanBeReplacedByConstexprIf/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppImplicitDefaultConstructorNotAvailable/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompatiblePointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompleteSwitchStatement/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInconsistentNaming/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIntegralToPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInvalidLineContinuation/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppJoinDeclarationAndAssignment/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLambdaCaptureNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableWithNonTrivialDtorIsNeverUsed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLongFloat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeConst/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeStatic/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberInitializersOrder/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMismatchedClassTags/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMissingIncludeGuard/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMissingKeywordThrow/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppModulePartitionWithSeveralPartitionUnits/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtAddressOfClassRValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtBindingRValueToLvalueReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtCopyElisionInCopyInitDeclarator/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtDoubleUserConversionInCopyInit/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtNotInitializedStaticConstLocalVar/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtReinterpretCastFromNullptr/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMultiCharacterLiteral/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMultiCharacterWideLiteral/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMustBePublicVirtualToImplementInterface/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMutableSpecifierOnReferenceMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNoDiscardExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNodiscardFunctionWithoutReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExceptionSafeResourceAcquisition/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExplicitConversionOperator/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExplicitConvertingConstructor/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonInlineFunctionDefinitionInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonInlineVariableDefinitionInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNotAllPathsReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppObjectMemberMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOutParameterMustBeWritten/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConstPtrOrRef/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNamesMismatch/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPassValueParameterByConstReference/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPointerConversionDropsQualifiers/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPointerToIntegralConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPolymorphicClassWithNonVirtualPublicDestructor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyErroneousEmptyStatements/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyUnintendedObjectSlicing/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrecompiledHeaderIsNotIncluded/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrecompiledHeaderNotFound/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfBadFormat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfExtraArg/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfMissedArg/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfRiskyFormat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrivateSpecialMemberFunctionIsNotImplemented/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRangeBasedForIncompatibleReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedefinitionOfDefaultArgumentInOverrideFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantAccessSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBaseClassAccessSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBaseClassInitializer/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBooleanExpressionArgument/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantCastExpression/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantComplexityInComparison/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantConditionalExpression/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantConstSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantControlFlowJump/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantDereferencingAndTakingAddress/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElaboratedTypeSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElseKeyword/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElseKeywordInsideCompoundStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantEmptyDeclaration/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantEmptyStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantExportKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantFwdClassOrEnumSpecifier/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantInlineSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantLambdaParameterList/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantMemberInitializer/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantNamespaceDefinition/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantParentheses/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantQualifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantQualifierADL/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantStaticSpecifierOnMemberAllocationFunction/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantStaticSpecifierOnThreadLocalLocalVariable/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTemplateArguments/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTemplateKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTypenameKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantVoidArgumentList/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantZeroInitializerInAggregateInitialization/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReinterpretCastFromVoidPtr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRemoveRedundantBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReplaceMemsetWithZeroInitialization/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReplaceTieWithStructuredBinding/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReturnNoValueInNonVoidFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSmartPointerVsMakeFunction/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSomeObjectMembersMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSpecialFunctionWithoutNoexceptSpecification/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticAssertFailure/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppThrowExpressionCanBeReplacedWithRethrow/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTooWideScope/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTooWideScopeInitStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTypeAliasNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUninitializedDependentBaseClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUninitializedNonStaticDataMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnionMemberOfReferenceType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAssociativeContains/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAuto/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAutoForNumeric/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseElementsView/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseEraseAlgorithm/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseFamiliarTemplateSyntaxForGenericLambdas/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseRangeAlgorithm/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseStdSize/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseStructuredBinding/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseTypeTraitAlias/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUserDefinedLiteralSuffixDoesNotStartWithUnderscore/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUsingResultOfAssignmentAsCondition/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVariableCanBeMadeConstexpr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVirtualFunctionCallInsideCtor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVirtualFunctionInFinalClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVolatileParameterInDeclaration/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWarningDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWrongIncludesOrder/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWrongSlashesInIncludeDirective/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppZeroConstantCanBeReplacedWithNullptr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppZeroValuedExpressionUsedAsNullPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=IdentifierTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=IfStdIsConstantEvaluatedCanBeReplaced/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StdIsConstantEvaluatedWillAlwaysEvaluateToConstant/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringLiteralTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppClangFormat/EnableClangFormatSupport/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_ARGUMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_BINARY_EXPRESSIONS_CHAIN/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXPRESSION/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_FOR_STMT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_PARAMETER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_ARGUMENT/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_PARAMETER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTIPLE_DECLARATION/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_TERNARY/@EntryValue" value="ALIGN_ALL" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_CLASS_DEFINITION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_DECLARATIONS/@EntryValue" value="0" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DECLARATION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DEFINITION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BREAK_TEMPLATE_DECLARATION/@EntryValue" value="LINE_BREAK" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CASE_BLOCK_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CONTINUOUS_LINE_INDENT/@EntryValue" value="Double" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_ACCESS_SPECIFIERS_FROM_CLASS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CASE_FROM_SWITCH/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CLASS_MEMBERS_FROM_ACCESS_SPECIFIERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_COMMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_SIZE/@EntryValue" value="4" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_STYLE/@EntryValue" value="Space" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INITIALIZER_BRACES/@EntryValue" value="END_OF_LINE_NO_SPACE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INT_ALIGN_EQ/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INVOCABLE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_CODE/@EntryValue" value="2" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue" value="2" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_USER_LINEBREAKS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/LINE_BREAK_AFTER_COLON_IN_MEMBER_INITIALIZER_LISTS/@EntryValue" value="ON_SINGLE_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/MEMBER_INITIALIZER_LIST_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_INDENTATION/@EntryValue" value="All" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/OTHER_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_CATCH_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_ELSE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_NAMESPACE_DEFINITIONS_ON_SAME_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_WHILE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SIMPLE_BLOCK_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_ARGS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_PARAMS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_SEMICOLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBERS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_METHOD/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_NESTED_DECLARATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBERS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_METHOD/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_UNARY_OPERATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_COLON/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_SEMICOLON/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_METHOD/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_METHOD/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BETWEEN_CLOSING_ANGLE_BRACKETS_IN_TEMPLATE_ARGS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_ARRAY_ACCESS_BRACKETS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_DECLARATION_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_BLOCKS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_METHOD_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPECIAL_ELSE_IF_TREATMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TAB_WIDTH/@EntryValue" value="4" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TYPE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_BINARY_OPSIGN/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_ARGUMENTS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_RPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_RPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_TERNARY_OPSIGNS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_PARAMETERS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
<option name="/Default/CodeStyle/EditorConfig/EnableClangFormatSupport/@EntryValue" value="false" type="bool" />
</component>
</project>

View File

@@ -1,11 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="GrazieInspection" enabled="false" level="GRAMMAR_ERROR" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

View File

@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CIDR" type="CPP_MODULE" version="4" />

7
.idea/misc.xml generated
View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakePythonSetting">
<option name="pythonIntegrationState" value="YES" />
</component>
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/linuxSmartie.iml" filepath="$PROJECT_DIR$/.idea/linuxSmartie.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -1,54 +0,0 @@
cmake_minimum_required(VERSION 3.20)
project(linuxSmartie
VERSION 1.0
LANGUAGES CXX
)
# Use C++20
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Collect sources
file(GLOB SOURCES
src/*.cpp
main.cpp
)
# Define executable
add_executable(linuxSmartie ${SOURCES})
# Include directories
target_include_directories(linuxSmartie
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# Compiler warnings
target_compile_options(linuxSmartie
PRIVATE
-O2
-Wall
-Wextra
-Wpedantic
-Werror
-fdiagnostics-color=always
)
find_package(libwebsockets QUIET)
find_package(ZLIB QUIET)
if(libwebsockets_FOUND)
target_link_libraries(linuxSmartie PRIVATE websockets)
target_compile_definitions(linuxSmartie PRIVATE ENABLE_HOME_ASSISTANT)
else()
message(STATUS "no libwebsockets found, disabling hass")
endif()
if(ZLIB_FOUND)
target_link_libraries(linuxSmartie PRIVATE z)
target_compile_definitions(linuxSmartie PRIVATE ENABLE_ZLIB)
else()
message(STATUS "no zlib found, disabling saving logs to gz")
endif()

View File

@@ -1,27 +1,92 @@
# wrapper around cmake, see CMakeLists.txt for compile options CC := cc
.PHONY: configure build clean nuke install install_service PKG_CONFIG := pkg-config
BUILD_DIR ?= build TARGET := build/linuxSmartie
build: configure BITCHY_ERRORS := \
cmake --build ${BUILD_DIR} -j -Wconversion \
-Wsign-conversion \
-Wshadow \
-Wformat=2 \
-Wundef \
-Wstrict-prototypes \
-Wmissing-prototypes \
-Wold-style-definition \
-Wwrite-strings \
-Wvla \
-Wcast-qual \
-Wcast-align \
-Wpointer-arith \
-Wswitch-enum \
configure: CFLAGS := \
cmake -S . -B ${BUILD_DIR} -std=c23 \
-Iinclude \
-Ibuild \
-D_GNU_SOURCE \
-D_POSIX_C_SOURCE=200809L \
-Wall \
-Wextra \
-Wpedantic \
-Werror \
-pedantic-errors \
-MMD -MP \
-O2 \
$(BITCHY_ERRORS)
LDLIBS := \
$(shell $(PKG_CONFIG) --libs libusb-1.0) \
-lm
SOURCES := $(wildcard src/*.c)
OBJECTS := $(patsubst src/%.c,build/src/%.o,$(SOURCES))
DEPS := $(OBJECTS:.o=.d)
VERSION_H := build/version.h
# glowny target, linkuje linuxSmartie, potrzebuje ${OBJECTS}, jest na samej górze wiec jest domyslny
$(TARGET): $(OBJECTS) $(VERSION_H)
$(CC) $(OBJECTS) -o $@ $(LDLIBS)
# Compile source files
build/src/%.o: src/%.c $(VERSION_H)
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
# Generate version.h from Git
$(VERSION_H): include/version.h.in
@mkdir -p $(dir $@)
@version=$$(git describe --always --long --dirty 2>/dev/null || echo unknown); \
sed "s/@VCS_TAG@/$$version/" $< > $@.tmp; \
if ! cmp -s $@.tmp $@ 2>/dev/null; then \
mv $@.tmp $@; \
else \
rm -f $@.tmp; \
fi
# Include automatically generated header dependencies
-include $(DEPS)
.PHONY: all clean install uninstall debug
all: $(TARGET)
debug: CFLAGS += -O0 -g
debug: $(TARGET)
clean: clean:
cmake --build $(BUILD_DIR) --target clean rm -rf build
nuke: clean install: $(TARGET)
rm -rf $(BUILD_DIR) install -Dm755 $(TARGET) $(DESTDIR)$(HOME)/.local/bin/linuxSmartie
install: build service_install: install
mkdir -p ${HOME}/.local/bin
install ${BUILD_DIR}/linuxSmartie ${HOME}/.local/bin
install_service: install
mkdir -p ~/.config/systemd/user mkdir -p ~/.config/systemd/user
cp linuxSmartie.service ~/.config/systemd/user/linuxSmartie.service cp linuxSmartie.service ~/.config/systemd/user/linuxSmartie.service
systemctl --user daemon-reload systemctl --user daemon-reload
systemctl --user enable linuxSmartie systemctl --user enable linuxSmartie
systemctl --user start linuxSmartie systemctl --user start linuxSmartie
uninstall:
rm -f $(DESTDIR)$(HOME)/.local/bin/linuxSmartie

View File

@@ -1,29 +1,23 @@
# linuxSmartie # linuxSmartie
Alphanumeric LCD controller software for Linux (kind of LCDSmartie for Windows but no GUI, no config file, no plugins, no Windows). Alphanumeric LCD controller software for Linux (kind of LCDSmartie for Windows but no GUI, no config file, no plugins, no Windows).
~~Written with C++26 (`-std=c++2c`), will need at least gcc 14 to compile.~~ Uhm actually written with C++20 now, I was only really using `std::print` from 26. Let us not fall for memes. Will need gcc 10 to compile. GCC actually made C++20 the default since v16. - Hardcoded to work with 4x40 character display because that's what I have. Uses Matrix Orbital protocol over serial. Should work with most other display sizes by modifying `src/lcd.c`. If you're trying to make it work with anything else, good luck.
- Originally written in C++26, then in C++20, and now finally in C23 because GOD C++ IS A MESS. Used to be vaguely C99 at some point but meson didn't like it and ig writing C23 I'm consistent with esp-idf v6.0.
We can't go lower than C++20, we use too much shit introduced by it (std::format, std::chrono stuff mostly). - Vaguely inspired by suckless software philosophy, as in:
Hardcoded to work with 4x40 character display because that's what I have and I don't plan on using anything else. Uses Matrix Orbital protocol over serial. Should work with most other display sizes by modifying `src/LCDDisplay.cpp`. Best of luck if you try to make it work with anything else.
Vaguely inspired by suckless software philosophy, as in:
## The config is the source code ## The config is the source code
Same as plugins. Just write in more features scrub, learn some C++, damn Same as plugins. Just write in more features scrub, learn some C, damn
## TODOs ## TODOs
- Use `std::string_view` or `const std::string &` when possible, its faster, no copy - GPU power is jumpy, average? Use accumulator instead?
- Extra screens support perhaps? just halt sensors monitoring thread and print another struct on display if needed, have a semaphore or sth - Extra screens support perhaps? just halt sensors monitoring thread and print another struct on display if needed, have a semaphore or sth
- Maybe not even screens, keep the 1st line and change lines 2-4 - Maybe not even screens, keep the 1st line and change lines 2-4
- Media playback screen (use mpris, get playback info with mpris) - Media playback screen (use mpris, get playback info with mpris)
- In the LCDDisplay lib, reimplement all the features I have in picoSmartie + maybe finally add keypad support for both so I can toggle backlight (?) or change screens if I want any - In the lcd lib, reimplement all the features I have in picoSmartie + maybe finally add keypad support for both so I can toggle backlight (?) or change screens if I want any
- Screens could be automatic tbh, like if it detects media playing it switches to media screen, if in media screen and GPU power > 80W it switches to main (hwmon) screen - Screens could be automatic tbh, like if it detects media playing it switches to media screen, if in media screen and GPU power > 80W it switches to main (hwmon) screen
- Try to be C++, avoid globals, wrap in namespaces, use naming conventions, use classes, avoid pure C way of doing things unless it makes more sense (which it often does tbh, better for reading small files)
- **FUCK C++, HUGE REWRITE FROM SCRATCH IN C LIKE GOD ALWAYS INTENDED** but fuuuuck display thread would be so painful without std::string. And I really would need to start a whole ass project from a clean slate since I made it a point to use a lot of C++ features which now bite me in the ass.
- Fuck it man, nvm let's leave it. Make it C++17 or 20 or sth so it compiles on yo mama or sth.
## LCD layout ## LCD layout (on Windows)
It looks something like this It looks something like this
``` ```
**************************************** ****************************************
@@ -33,7 +27,6 @@ It looks something like this
1,200V 1186MHz 3150 2/9MHz 0,112V 1,200V 1186MHz 3150 2/9MHz 0,112V
**************************************** ****************************************
``` ```
(on LCDSmartie)
### Ok but what does it all mean ### Ok but what does it all mean
In general left side is CPU/system stuff, right side is GPU stuff In general left side is CPU/system stuff, right side is GPU stuff
@@ -59,20 +52,19 @@ Text04="$dll(hwinfo,1,Vcore,3)V $Right($dll(hwinfo,1,Average Effective Clock,a)M
### So what's still missing ### So what's still missing
- Per-core CPU load, doable but annoying - Per-core CPU load, doable but annoying
- FPS counter, maybe possible? - FPS counter, maybe possible? Not really on wayland, by design
- Active core count, probably impossible but loadAvg is better? - Active core count, probably impossible but loadAvg is better
- GPU board power, probably impossible, it was estimated on windows anyway - GPU board power, probably impossible, it was estimated on windows anyway
- CPU cache/iod, likely impossible, zenpower doesnt show it - CPU cache/iod, likely impossible, zenpower doesnt show it
- GPU clock limit, probably impossible, gpu_metrics doesnt show it? - GPU clock limit, probably impossible, gpu_metrics doesnt show it?
### Shit needed to compile on Debian 13 ### Shit needed to compile on Debian 13
V simple:
```bash ```bash
sudo apt install build-essential cmake libwebsockets-dev nlohmann-json3-dev sudo apt install build-essential meson libusb-1.0-0-dev pkgconf zlib1g-dev
make configure make -j
make build
``` ```
You're probably on some meme immutable distro so use Debian 13 on distrobox. Ubuntu 24.04 should also work. Might set up a distrobox container file, with distrobox-assemble (?) You're probably on some meme immutable distro so use Debian 13 on distrobox. Any distro with GCC and cmake should work tbh.
### Additional notes ### Additional notes
@@ -80,4 +72,4 @@ You're probably on some meme immutable distro so use Debian 13 on distrobox. Ubu
- On Fedora Workstation it can be added by appending it to `/etc/kernel/cmdline`. - On Fedora Workstation it can be added by appending it to `/etc/kernel/cmdline`.
- On atomic desktops (Silverblue/Kinoite), `rpm-ostree kargs --append-if-missing=acpi_enforce_resources=lax` - On atomic desktops (Silverblue/Kinoite), `rpm-ostree kargs --append-if-missing=acpi_enforce_resources=lax`
- You need to be able to read RAPL as user. Create in `/etc/tmpfiles.d/intelrapl.conf` with content `z- /sys/class/powercap/intel-rapl:0/energy_uj 0440 root uucp - -`. Adjust uucp to whatever group applicable, like `dialout`. - You need to be able to read RAPL as user. Create in `/etc/tmpfiles.d/intelrapl.conf` with content `z- /sys/class/powercap/intel-rapl:0/energy_uj 0440 root uucp - -`. Adjust uucp to whatever group applicable, like `dialout`.
- `compile_commands.json` is so clang understands this project, so we can edit it with helix/neovim. Generated with `bear -- make`, make sure to run `make clean` beforehand. - ModemManager might steal the port sometimes. Either disable the service or setup an udev rule: `SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000a", ENV{ID_MM_DEVICE_IGNORE}="1"`

View File

@@ -1,7 +1,6 @@
[linuxsmartie] [linuxsmartie]
additional_packages="build-essential cmake" additional_packages="build-essential libusb-1.0-0-dev pkgconf zlib1g-dev"
additional_packages="libwebsockets-dev nlohmann-json3-dev" image=quay.io/toolbx-images/debian-toolbox:13
image=debian-toolbox:13
init=false init=false
replace=true replace=true
start_now=true start_now=true

View File

@@ -1,48 +0,0 @@
#ifndef LCDDISPLAY_HPP
#define LCDDISPLAY_HPP
#define LCD_LINES 4
#define LCD_COLS 40
#include <array>
#include <cstdint>
#include <string>
class LCDDisplay {
private:
int lcd = -1;
uint32_t baud = 0;
std::string dev;
std::array<char, LCD_LINES * LCD_COLS + 8> writebuf{}; // 4x40 chars + 2x4 setcursor commands
static constexpr bool dualDisp = (LCD_LINES * LCD_COLS > 80); // is it dual display device or not, evauluated from the size at compile time
int open(bool reopen = false);
void write(const uint8_t* cmdbuf, uint8_t len);
void close();
// we avoid using these, use buffer (even if these are faster for small writes, perhaps a rewrite is in order)
void writeStr(const std::string& str);
void writeStr(uint8_t row, uint8_t col, const std::string& str);
public:
explicit LCDDisplay(const std::string& dev, uint32_t baud = 0010002); // from termios-baud.h, B115200
~LCDDisplay();
int Init();
void clear();
void setContrast(uint8_t contrast);
void setBacklight(bool enabled);
void setBacklight(uint8_t brightness);
void setCursor(uint8_t row, uint8_t col);
void setCustomChar(uint8_t index, std::array<uint8_t, 8> cchar);
void writeBuf(uint8_t row, uint8_t col, const std::string& str);
void clearBuf();
void flushBuf();
char bar(uint8_t char_id, double value);
// returns custom char no. c, small enough to define in header
static constexpr char cc(const uint8_t c) { return static_cast<char>(c % 8); }
};
#endif

67
include/aio.h Normal file
View File

@@ -0,0 +1,67 @@
#ifndef LINUXSMARTIE_AIO_H
#define LINUXSMARTIE_AIO_H
#include <libusb-1.0/libusb.h>
#define AIO_USB_VID 0x1b1cU
#define AIO_USB_PID 0x0c13U
#define AIO_COLOR_CYAN { 0, 255, 255 }
#define AIO_COLOR_WHITE { 255, 255, 255 }
#define AIO_COLOR_OFF { 0, 0, 0 }
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
struct aio_params_t {
struct {
uint8_t min_duty;
uint8_t max_duty;
bool fan_stop;
float setpoint;
struct {
float Ki;
float Kd;
} ctrl;
} fans;
};
enum aio_pump_mode_t {
AIO_PUMP_MODE_QUIET,
AIO_PUMP_MODE_BALANCED,
AIO_PUMP_MODE_EXTREME,
AIO_PUMP_MODE_UNKNOWN
};
enum aio_color_mode_t : uint8_t {
AIO_COLOR_PULSE,
AIO_COLOR_SHIFT,
AIO_COLOR_FIXED,
AIO_COLOR_BLINKING
};
struct rgb_t {
uint8_t r;
uint8_t g;
uint8_t b;
};
struct curve_point_t {
uint8_t temp;
uint8_t duty;
};
struct aio_t {
libusb_device_handle* handle;
enum aio_pump_mode_t pump_mode;
float water_temp;
int16_t pump_counter;
uint16_t pump_speed;
uint16_t fan_speed[2];
};
void aio_detect(struct aio_t *aio);
void aio_update(struct aio_t *aio);
void aio_close(const struct aio_t *aio);
#endif

View File

@@ -1,71 +0,0 @@
#ifndef APP_HPP
#define APP_HPP
#include <atomic>
#include <cpu.hpp>
#include <gpu.hpp>
#include <mutex>
#include <semaphore>
#include <stop_token>
#include <sys.hpp>
#include <thread>
class Application {
public:
struct Config {
struct Lcd {
uint16_t refresh_interval_ms;
uint8_t contrast;
std::string port;
};
struct HomeAssistant {
std::string url;
std::string entity;
std::string token;
};
Lcd lcd;
HomeAssistant hass;
std::string cpu_pciaddr;
std::string gpu_pciaddr;
std::string sys_sensor;
std::string sys_ifname;
std::string home_dir;
};
explicit Application(Config config);
~Application();
int Run();
private:
struct Sensors {
sys::TimeDate td;
double loadavg[3] = {0.0f};
sys::Memory ram;
sys::Disk home;
sys::Network net;
sys::sysSensors sys;
cpu::cpuSensors cpu;
gpu::gpuSensors gpu;
double hass_state = 0.0f;
};
struct Control {
std::binary_semaphore display{0};
std::mutex sensors; // making sure the sensors struct isnt accessed by multiple threads at once
sigset_t sigset;
};
pid_t m_mainPid{};
int m_ret = 0;
Config m_config{}; // m_ is prefix for member variable
Control m_control{};
Sensors m_sensors{};
void hassThread(const std::stop_token& st);
void sensorsThread(const std::stop_token& st);
void displayThread(const std::stop_token& st);
void Crash(int ret);
};
#endif

30
include/cpu.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef LINUXSMARTIE_CPU_H
#define LINUXSMARTIE_CPU_H
#include <stdint.h>
enum cpu_module_t : uint8_t {
unknown,
k10temp,
zenpower
};
struct cpu_sensors_t {
enum cpu_module_t module;
uint8_t num_cores;
int16_t tdie;
int16_t tctl;
int16_t tccd1;
// double pcore;
// double psoc;
float ptotal;
// double vcore;
// double vsoc;
uint32_t avg_freq;
char hwmon[64];
};
void cpu_detect(struct cpu_sensors_t *t, const char *pci_dev);
void cpu_update(struct cpu_sensors_t *c);
#endif

View File

@@ -1,33 +0,0 @@
#ifndef CPU_HPP
#define CPU_HPP
#include <cstdint>
#include <string>
namespace cpu {
enum Module : uint8_t { unknown,
k10temp,
zenpower };
struct cpuSensors {
Module module = unknown;
uint8_t num_cores = 0;
int16_t tdie = 0;
int16_t tctl = 0;
int16_t tccd1 = 0;
// double pcore = 0.0;
// double psoc = 0.0;
float ptotal = 0.0;
// double vcore = 0.0;
// double vsoc = 0.0;
uint32_t avg_freq = 0;
std::string hwmon;
};
int detect(cpuSensors* target, const std::string& pci);
void update(cpuSensors* c);
} // namespace cpu
#endif

41
include/gpu.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef LINUXSMARTIE_GPU_H
#define LINUXSMARTIE_GPU_H
#include <stdint.h>
struct gpu_sensors_t {
uint64_t vram_used;
// uint64_t vram_total = 0;
float vcore;
uint16_t clock_core;
uint16_t clock_mem;
// uint8_t fan_pwm = 0;
uint16_t fan_speed;
uint16_t power_avg;
uint16_t power_cap;
// uint16_t power_cap_max = 0;
uint8_t temp_edge;
uint8_t temp_hotspot;
uint8_t temp_mem;
uint8_t temp_vrm_gfx;
uint8_t temp_vrm_soc;
uint8_t temp_vrm_mem;
bool has_gpu_metrics;
uint8_t usage_core;
uint8_t usage_media;
uint8_t usage_umc;
char hwmon[64];
};
void gpu_detect(struct gpu_sensors_t *t, const char *pci);
void gpu_update(struct gpu_sensors_t *g);
#endif

View File

@@ -1,85 +0,0 @@
#ifndef GPU_HPP
#define GPU_HPP
#include <cstdint>
#include <string>
/* clean abstraction for gpu_metrics or metrics in filesystem in sane units */
namespace gpu {
struct gpuSensors {
uint64_t vram_used = 0;
// uint64_t vram_total = 0;
float vcore = 0.0f;
uint16_t clock_core = 0;
uint16_t clock_mem = 0;
// uint8_t fan_pwm = 0;
uint16_t fan_speed = 0;
uint16_t power_avg = 0;
uint16_t power_cap = 0;
// uint16_t power_cap_max = 0;
uint8_t temp_edge = 0;
uint8_t temp_hotspot = 0;
uint8_t temp_mem = 0;
uint8_t temp_vrm_gfx = 0;
uint8_t temp_vrm_soc = 0;
uint8_t temp_vrm_mem = 0;
bool hasGpuMetrics = false;
uint8_t usage_core = 0;
uint8_t usage_media = 0;
uint8_t usage_umc = 0;
std::string hwmon;
};
int detect(gpuSensors* target, const std::string& pci);
void update(gpuSensors* g);
} // namespace gpu
// class ThrottleStatus(IntFlag):
// # linux/drivers/gpu/drm/amd/pm/inc/amdgpu_smu.h
// PPT0 = 1 << 0
// PPT1 = 1 << 1
// PPT2 = 1 << 2
// PPT3 = 1 << 3
// SPL = 1 << 4
// FPPT = 1 << 5
// SPPT = 1 << 6
// SPPT_APU = 1 << 7
// TDC_GFX = 1 << 16
// TDC_SOC = 1 << 17
// TDC_MEM = 1 << 18
// TDC_VDD = 1 << 19
// TDC_CVIP = 1 << 20
// EDC_CPU = 1 << 21
// EDC_GFX = 1 << 22
// APCC = 1 << 23
// TEMP_GPU = 1 << 32
// TEMP_CORE = 1 << 33
// TEMP_MEM = 1 << 34
// TEMP_EDGE = 1 << 35
// TEMP_HOTSPOT = 1 << 36
// TEMP_SOC = 1 << 37
// TEMP_VR_GFX = 1 << 38
// TEMP_VR_SOC = 1 << 39
// TEMP_VR_MEM0 = 1 << 40
// TEMP_VR_MEM1 = 1 << 41
// TEMP_LIQUID0 = 1 << 42
// TEMP_LIQUID1 = 1 << 43
// VRHOT0 = 1 << 44
// VRHOT1 = 1 << 45
// PROCHOT_CPU = 1 << 46
// PROCHOT_GFX = 1 << 47
// PPM = 1 << 56
// FIT = 1 << 57
#endif

View File

@@ -1,7 +1,7 @@
#ifndef GPU_METRICS_HPP #ifndef GPU_METRICS_H
#define GPU_METRICS_HPP #define GPU_METRICS_H
#include <cstdint> #include <stdint.h>
/* STRUCTS COPIED WHOLESALE FROM LINUX SOURCE CODE, formatted and wrapped around a namespace /* STRUCTS COPIED WHOLESALE FROM LINUX SOURCE CODE, formatted and wrapped around a namespace
https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/include/kgd_pp_interface.h */ https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/include/kgd_pp_interface.h */
@@ -16,8 +16,6 @@ https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/amd/include/kgd_pp
#define MAX_XCC 8 #define MAX_XCC 8
#define NUM_XCP 8 #define NUM_XCP 8
namespace metrics {
struct metrics_table_header { struct metrics_table_header {
uint16_t structure_size; uint16_t structure_size;
uint8_t format_revision; uint8_t format_revision;
@@ -795,6 +793,5 @@ struct gpu_metrics_v3_0 {
/* Metrics table alpha filter time constant [us] */ /* Metrics table alpha filter time constant [us] */
uint32_t time_filter_alphavalue; uint32_t time_filter_alphavalue;
}; };
} // namespace metrics
#endif #endif

View File

@@ -1,37 +0,0 @@
#ifndef HASS_HPP
#define HASS_HPP
#ifdef ENABLE_HOME_ASSISTANT
#include <libwebsockets.h>
#include <cstdint>
#include <string>
class HomeAssistant {
public:
struct Config {
std::string address;
std::string entity;
std::string token;
};
explicit HomeAssistant(const Config& config);
~HomeAssistant();
float state = 0.0f;
int connect();
void disconnect() const;
void service(int timeout_ms = 0) const;
private:
uint16_t id = 2;
std::string tx;
lws_context* context = nullptr;
lws* websock = nullptr;
void event_handler(lws* wsi, lws_callback_reasons reason, char* in);
static int callback(lws* wsi, lws_callback_reasons reason, void* user, void* in, size_t len);
Config m_config;
};
#endif
#endif

34
include/lcd.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef LINUXSMARTIE_LCD_H
#define LINUXSMARTIE_LCD_H
#define LCD_LINES 4
#define LCD_COLS 40
#include <stdint.h>
struct lcd_t {
int fd;
uint32_t baud;
struct {
char cchar[8][11]; // 8 custom chars, each 11 bytes (3 byte command + 8 byte glyph)
char text[LCD_LINES * LCD_COLS + 8]; // number of chars on the lcd + 2*4 byte set cursor commands
} writebuf;
char port[96];
};
typedef uint8_t lcd_glyph_t[8];
void lcd_close(struct lcd_t *lcd);
void lcd_clear(struct lcd_t *lcd);
void lcd_set_contrast(struct lcd_t *lcd, uint8_t contrast);
void lcd_set_backlight(struct lcd_t *lcd, bool enabled);
void lcd_set_cursor(struct lcd_t *lcd, uint8_t row, uint8_t col);
void lcd_set_custom_char(struct lcd_t *lcd, uint8_t index, const lcd_glyph_t *glyph);
void lcd_write_buf(struct lcd_t *lcd, uint8_t row, uint8_t col, const char *str);
void lcd_writef(struct lcd_t *lcd, uint8_t x, uint8_t y, const char *fmt, ...) __attribute__((format(printf, 4, 5)));
void lcd_clear_buf(struct lcd_t *lcd);
void lcd_flush_buf(struct lcd_t *lcd);
char lcd_bar(struct lcd_t *lcd, uint8_t char_id, double value);
int lcd_init(struct lcd_t *lcd);
#endif

36
include/log.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef LINUXSMARTIE_LOG_H
#define LINUXSMARTIE_LOG_H
/* ── kill switch ────────────────────────────────────────────────────────── */
/* comment out to disable all logging */
#define APPLOG_ENABLED
/* ─────────────────────────────────────────────────────────────────────── */
#ifdef APPLOG_ENABLED
typedef enum {
LOG_INF,
LOG_WRN,
LOG_ERR,
LOG_DBG,
LOG_START,
} log_level_t;
/* __PRETTY_FUNCTION__ is a GCC/Clang extension that gives the full
signature: "voi*d foo(int x)" — same thing source_location gave you.
Falls back to plain __func__ (C99) if somehow unavailable. */
#define applog(level, fmt, ...) \
log_write(level, __func__, fmt, ##__VA_ARGS__)
/* format attribute gives compile-time printf format checking —
better than wha*t std::format gave you at runtime */
void log_write(log_level_t level, const char *func, const char *fmt, ...)
__attribute__((format(printf, 3, 4)));
#else /* APPLOG_ENABLED not defined */
#define applog(...) ((void)0)
#endif /* APPLOG_ENABLED */
#endif /* LINUXSMARTIE_LOG_H */

View File

@@ -1,96 +0,0 @@
#ifndef LOG_HPP
#define LOG_HPP
// comment out to disable logging in general
#define applog(level, fmt, ...) utils::log(level, std::source_location::current(), fmt, ##__VA_ARGS__)
// comment out to disable logging to file
// #define LOG_FILE_LOCATION "linuxSmartie.log.gz"
#ifdef applog
#include <chrono>
#include <format>
#include <iostream>
#include <mutex>
#include <source_location>
#include <string_view>
#ifdef LOG_FILE_LOCATION
#include <zlib.h>
inline std::mutex fmtx; // file logging mutex
#endif
enum class LOG { INF, WRN, ERR, DBG, START };
namespace utils {
inline std::mutex cmtx; // console logging mutex
constexpr std::string pFname(std::string_view full) {
if (const auto pos = full.find(' '); pos != std::string_view::npos)
full.remove_prefix(pos + 1);
if (const auto pos = full.rfind('('); pos != std::string_view::npos)
full.remove_suffix(full.size() - pos);
return std::string(full);
}
template <typename... Args>
void log(const LOG level, const std::source_location& loc, std::format_string<Args...> str, Args&&... args) {
constexpr std::string_view reset = "\033[0m";
constexpr std::string_view pattern = "[{}{}{}] ";
std::string_view color;
switch (level) {
case LOG::INF:
color = "\033[1;32m";
break; // bold; green
case LOG::WRN:
color = "\033[1;33m"; // bold; yellow
break;
case LOG::ERR:
color = "\033[1;31m"; // bold; red
break;
case LOG::DBG:
break;
case LOG::START:
color = "\033[1;3;36m"; // bold; blinking; cyan
break;
}
/* log to console */
{
std::scoped_lock lock(cmtx);
std::cout << std::format(pattern, color, pFname(loc.function_name()), reset);
std::cout << std::format(str, std::forward<Args>(args)...) << std::endl;
}
#ifdef LOG_FILE_LOCATION
/* log to file */
{
auto now = std::chrono::system_clock::now();
auto local = std::chrono::current_zone()->to_local(now);
std::string msg = std::format("{:%x %H:%M:%S }", floor<std::chrono::milliseconds>(local)); // timestamp
std::format_to(std::back_inserter(msg), pattern, color, pFname(loc.function_name()), reset); // tag
std::format_to(std::back_inserter(msg), str, std::forward<Args>(args)...); // actual log message
std::scoped_lock lock(fmtx);
gzFile file = gzopen(LOG_FILE_LOCATION, "ab");
if (file) {
gzprintf(file, "%s\n", msg.c_str());
gzclose(file);
}
}
#endif
}
} // namespace utils
#else
#define applog(...)
#endif
#endif

56
include/sys.h Normal file
View File

@@ -0,0 +1,56 @@
#ifndef LINUXSMARTIE_SYS_H
#define LINUXSMARTIE_SYS_H
#include "utils.h"
#include <stdint.h>
#define SYS_GET_TIME_STR(t) sys_get_time_string((char[16]){0}, 16, t)
#define SYS_GET_DATE_STR(t) sys_get_date_string((char[16]){0}, 16, t)
struct memory_t {
uint64_t free;
uint64_t used;
uint64_t total;
};
struct disk_t {
struct memory_t usage;
char mountpoint[64];
};
struct network_t {
uint32_t download;
uint32_t upload;
sensor_file_t f_rx;
sensor_file_t f_tx;
char device[16];
};
struct syssensors_t {
char hwmon[64];
// uint16_t fan_speed[1]; // just one fan (pump) we monitor
int16_t t_sensor; // ntc temp sensor
};
char *sys_get_time_string(char *buf, size_t bufsz, time_t t);
char *sys_get_date_string(char *buf, size_t bufsz, time_t t);
void sys_get_load_avg(double *load_avg);
void sys_get_distro(char* name, char* ver_str);
void sys_get_ram(struct memory_t *ram);
void sys_get_disk(struct disk_t *disk);
void sys_get_network(struct network_t *network);
void sys_netdev_detect(struct network_t *network);
void sys_detect(struct syssensors_t *s, const char *platform);
void sys_update_sensors(const struct syssensors_t *s);
#endif // LINUXSMARTIE_SYS_H

View File

@@ -1,49 +0,0 @@
#ifndef SYS_HPP
#define SYS_HPP
#include <cstdint>
#include <string>
namespace sys {
constexpr char months[][4] = {"sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paz", "lis", "gru"};
constexpr char wdays[][4] = {"Nie", "Pon", "Wto", "Sro", "Czw", "Pia", "Sob"};
struct sysSensors {
std::string hwmon;
uint16_t fan_speed[2] = {0}; // left and right cpu fans
int16_t t_sensor = 0; // ntc temp sensor
std::byte _pad[2];
};
struct Memory {
uint64_t free = 0;
uint64_t used = 0;
uint64_t total = 0;
};
struct Disk {
std::string mountpoint;
Memory usage;
};
struct TimeDate {
std::string time;
std::string date;
};
struct Network {
uint32_t download = 0;
uint32_t upload = 0;
};
void getTimeDate(TimeDate* td);
void getDisk(Disk* disk);
void getRam(Memory* mem);
void getNetwork(Network* net, const std::string& device);
int detect(sysSensors* s, const std::string& platform);
void updateSensors(sysSensors* s);
} // namespace sys
#endif

60
include/utils.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef LINUXSMARTIE_UTILS_H
#define LINUXSMARTIE_UTILS_H
#include <stdint.h>
#include <sys/types.h> // NOLINT yes its, needed for ssize_t
#if __STDC_VERSION__ >= 202000L
#define C_STD "C23"
#elif __STDC_VERSION__ >= 201710L
#define C_STD "C17"
#elif __STDC_VERSION__ >= 201112L
#define C_STD "C11"
#elif __STDC_VERSION__ >= 199901L
#define C_STD "C99"
#else
#define C_STD "C89"
#endif
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define P_BYTES(bytes, start) pretty_bytes((bytes), (start), (char[16]){0}, 16)
#define P_NUM(num) pretty_num((num), (char[16]){0}, 16)
typedef enum : uint8_t {
SENSOR_I16,
SENSOR_U8,
SENSOR_U16,
SENSOR_U64,
SENSOR_FLOAT,
} sensor_type_t;
typedef struct {
sensor_type_t type;
int fd;
void* value;
uint32_t divider;
} sensor_file_t;
int get_instance_lock();
void timespec_add_ms(struct timespec *ts, uint16_t ms);
int64_t timespec_diff_us(const struct timespec *a, const struct timespec *b);
char *pretty_num(double num, char *buf, size_t bufsz);
char *pretty_bytes(uint64_t bytes, int8_t start_suffix, char *buf, size_t bufsz);
int get_hwmon_dir(char *path, size_t bufsz);
void open_sensor(sensor_file_t* s, const char* dir, const char *file, void* value, uint32_t divider, sensor_type_t type);
void fast_read_sensor_into_mem(const sensor_file_t *s, void* val);
void fast_read_sensor(const sensor_file_t *s);
void close_sensor(const sensor_file_t* s);
#endif

View File

@@ -1,80 +0,0 @@
#ifndef UTILS_HPP
#define UTILS_HPP
#include <fcntl.h>
#include <unistd.h>
#include <array>
#include <charconv>
#include <cstdint>
#include <filesystem>
#include <log.hpp>
#include <string>
namespace utils {
constexpr char fill_char = ' ';
enum just : uint8_t { right, left };
std::string pad(std::string s, uint8_t len, just j = right);
std::string center(std::string str, uint8_t len);
std::string getFirstDir(const std::string_view& path);
inline std::string pNum(const auto num, const uint8_t decs = 0) {
static std::array<char, 20> buf;
auto [ptr, ec] = std::to_chars(buf.data(), buf.data() + buf.size(), static_cast<double>(num), std::chars_format::fixed, decs);
return {buf.data(), ptr};
}
/* converts large number in (k)bytes to human-readable numer with suffix and sane decimal */
std::string pBytes(const auto bytes, const bool isKbytes = false) {
double bytes_d = bytes;
for (constexpr std::array suffix_list = {'K', 'M', 'G', 'T', 'P', 'E', 'Z'}; const auto& suffix : suffix_list) {
if (suffix == 'K' && isKbytes) continue; // skip first loop if already in Kbytes
bytes_d /= 1024.0;
if (bytes_d < 1024) {
if (bytes_d > 100) return pNum(bytes_d) + suffix;
if (bytes_d > 10) return pNum(bytes_d, 1) + suffix;
return pNum(bytes_d, 2) + suffix;
}
}
return "++++";
}
/* reads file into a std::array buffer */
template <size_t N>
inline ssize_t readFile(const std::string& path, std::array<char, N>& buffer) {
const int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (fd < 0) {
applog(LOG::ERR, "Error reading {}", path);
return 0;
}
const ssize_t len = read(fd, buffer.data(), buffer.size());
if (len >= 0) buffer[len] = '\0';
close(fd);
return len;
}
/* reads a number fast and divides it if needed */
template <uint32_t Divider = 1, typename T, size_t N>
inline void fastReadNum(const std::string& path, std::array<char, N>& buf, T& result) {
readFile(path, buf);
if constexpr (Divider == 1) {
std::from_chars(buf.data(), buf.data() + N, result);
return;
}
uint64_t temp{};
std::from_chars(buf.data(), buf.data() + N, temp);
if constexpr (std::is_floating_point_v<T>)
result = temp / static_cast<T>(Divider);
else
result = static_cast<T>(temp / Divider);
}
inline bool fileExists(const std::string_view& path) {
return std::filesystem::exists(path);
}
} // namespace utils
#endif

2
include/version.h.in Normal file
View File

@@ -0,0 +1,2 @@
#pragma once
#define GIT_HASH "@VCS_TAG@"

View File

@@ -5,6 +5,7 @@ Description=HD44780 display service (linuxSmartie)
Type=simple Type=simple
ExecStart=%h/.local/bin/linuxSmartie ExecStart=%h/.local/bin/linuxSmartie
Restart=on-failure Restart=on-failure
RestartSec=5
[Install] [Install]
WantedBy=default.target WantedBy=graphical-session.target

View File

@@ -1,20 +0,0 @@
#include <app.hpp>
int main() {
Application::Config config;
config.lcd.port = "/dev/serial/by-id/usb-Raspberry_Pi_Pico_E6616407E37AA12F-if00"; // tis a symlink, can also be just /dev/ttyACM0)
config.lcd.refresh_interval_ms = 500;
config.lcd.contrast = 215;
config.cpu_pciaddr = "0000:00:18.3";
config.gpu_pciaddr = "0000:0b:00.0";
config.sys_sensor = "nct6775.656";
config.sys_ifname = "enp4s0f0";
config.home_dir = "/home/drath";
config.hass.url = "home.drath.win";
config.hass.entity = "sensor.gniazdko_komputer_moc";
config.hass.token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI0MzUxMTZiY2VjNTg0MTI3OGI4YmU0NTY4ZGQ5MzIyYiIsImlhdCI6MTc1NzQzMDI0MCwiZXhwIjoyMDcyNzkwMjQwfQ.6wKTlxEA0FLWfv-qW35vewUSvTxdVrZWqFwHT5Khu6E";
Application App(config);
return App.Run();
}

View File

@@ -1,154 +0,0 @@
#include "LCDDisplay.hpp"
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <array>
#include <cstdint>
#include <cstring>
#include <log.hpp>
#include <string>
/* lcd size hardcoded to 4x40 idgaf */
LCDDisplay::LCDDisplay(const std::string& dev, const uint32_t baud) {
this->dev = dev;
this->baud = baud;
}
LCDDisplay::~LCDDisplay() {
this->close();
}
int LCDDisplay::Init() {
if (const int ret = this->open(); ret < 0) {
this->close();
return -1;
}
struct termios term{};
// Setup the serial port 8 data bits, no parity, 1 stopbit, no flow control
term.c_cflag = baud | CS8 | CSTOPB | CLOCAL | CREAD;
term.c_iflag = 0;
term.c_oflag = 0;
term.c_lflag = 0;
tcflush(lcd, TCIFLUSH);
tcsetattr(lcd, TCSANOW, &term);
// init linebuf to spaces
this->clearBuf();
// disable underline and block cursor
constexpr uint8_t cmdbuf[] = {254, 75, 254, 84};
::write(lcd, &cmdbuf, sizeof(cmdbuf));
// clear display and set cursor home
this->clear();
return 0;
}
int LCDDisplay::open(const bool reopen) {
applog(LOG::INF, "Opening serial port at \"{}\"", dev);
if (lcd < 0 || reopen)
lcd = ::open(dev.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); // ::open calls the global open() instead of LCDDisplay::open()
if (lcd < 0) {
applog(LOG::ERR, "Error opening serial port \"{}\"", dev); // err
return -1;
}
return 0;
}
void LCDDisplay::close() {
if (lcd >= 0) {
::close(lcd);
applog(LOG::INF, "LCD port closed");
}
lcd = -1;
}
void LCDDisplay::write(const uint8_t* cmdbuf, const uint8_t len) {
if (::write(lcd, cmdbuf, len) != len) {
applog(LOG::ERR, "failed to execute ::write, lost access to port?");
this->open(true);
}
}
void LCDDisplay::clear() {
constexpr uint8_t cmdbuf[] = {254, 88};
this->write(cmdbuf, sizeof(cmdbuf));
}
void LCDDisplay::setContrast(const uint8_t contrast) {
applog(LOG::INF, "Set contrast to {}", contrast);
const uint8_t cmdbuf[] = {254, 80, contrast};
this->write(cmdbuf, sizeof(cmdbuf));
}
void LCDDisplay::setBacklight(const bool enabled) {
applog(LOG::INF, "Set backlight to {}", enabled);
const uint8_t cmdbuf[] = {254, static_cast<uint8_t>(enabled ? 66 : 70), 255};
this->write(cmdbuf, enabled ? 3 : 2);
}
/* still doesnt work? */
void LCDDisplay::setBacklight(const uint8_t brightness) {
applog(LOG::INF, "Set backlight brightness to {}", brightness);
const uint8_t cmdbuf[] = {254, static_cast<uint8_t>(brightness ? 66 : 70), 255, brightness}; // 3rd char is timer, 4th is brightness
this->write(cmdbuf, brightness ? 4 : 2);
}
void LCDDisplay::setCursor(const uint8_t row, const uint8_t col) {
const uint8_t cmdbuf[] = {254, 71, static_cast<uint8_t>(col + 1), static_cast<uint8_t>(row + 1)};
this->write(cmdbuf, sizeof(cmdbuf));
}
/* reconsider so im not writing every time. prepend custom chars to the write buffer? */
void LCDDisplay::setCustomChar(const uint8_t index, const std::array<uint8_t, 8> cchar) {
uint8_t cmdbuf[] = {254, 78, index, 0, 0, 0, 0, 0, 0, 0, 0};
std::memcpy(&cmdbuf[3], &cchar, sizeof(cchar));
this->write(cmdbuf, sizeof(cmdbuf));
}
/* immediately writes string to the lcd at current cursor pos */
void LCDDisplay::writeStr(const std::string& str) {
this->write(reinterpret_cast<const uint8_t*>(str.c_str()), str.size());
}
/* immediately writes string to the lcd at given cursor pos */
void LCDDisplay::writeStr(const uint8_t row, const uint8_t col, const std::string& str) {
this->setCursor(row, col);
this->write(reinterpret_cast<const uint8_t*>(str.c_str()), str.size());
}
/* writes string to write buffer at calculated pos */
void LCDDisplay::writeBuf(const uint8_t row, const uint8_t col, const std::string& str) {
const size_t len = std::min(str.size(), writebuf.size() - col);
uint8_t pos = 4 + col + (LCD_COLS * (row % 2)); // skip 4 bytes to leave room for set cursor command
if (row >= 2) pos += 2 * LCD_COLS + 4; // if we're writing to disp2, skip another 4 bytes for set cursor command and 2*40 bytes of disp 1
std::memcpy(&writebuf[pos], str.c_str(), len);
}
/* initializes the write buffer to spaces */
void LCDDisplay::clearBuf() {
std::memset(writebuf.data(), ' ', writebuf.size());
}
/* flushes writebuffer to lcd, hyperoptmized, flushes buffer and commands in a single write operation */
void LCDDisplay::flushBuf() {
constexpr uint8_t setDisp[] = {254, 71, 1, 1}; // set cursor to char 1, line 1
std::memcpy(&writebuf[0], setDisp, 4);
std::memcpy(&writebuf[2 * LCD_COLS + 4], setDisp, 4);
writebuf[2 * LCD_COLS + 7] = 3; // overwrite set cursor c1 l1 to c1 l3
this->write(reinterpret_cast<uint8_t*>(writebuf.data()), writebuf.size());
}
// i have a whole flushbuf func to avoid too many writes, but im writing on every bar, need to rethink this.
// we could have a separate pre-char buffer and put all the setCustomChar commands to that?
char LCDDisplay::bar(const uint8_t char_id, const double value) {
std::array<uint8_t, 8> b{};
b.fill(0b01110);
for (uint8_t i = 0; i < (1.0 - value) * 8; i++)
b[i] = 0;
this->setCustomChar(char_id, b);
return cc(char_id);
}

255
src/aio.c Normal file
View File

@@ -0,0 +1,255 @@
#include "aio.h"
#include "log.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
static constexpr uint16_t usb_timeout = 250;
static constexpr uint8_t buf_size = 32; // needs 32 bc of overflow issue on these aios?
static constexpr uint8_t max_curve_points = 7;
static uint8_t req[buf_size], resp[buf_size];
static const struct curve_point_t fan_curve[] = {
{ .temp = 35, .duty = 25 }, // ~350rpm
{ .temp = 40, .duty = 40 },
{ .temp = 46, .duty = 60 },
{ .temp = 50, .duty = 100 }
};
static void aio_usb_claim(libusb_device_handle *dev) {
int ret = libusb_claim_interface(dev, 0);
if (ret != 0) {
applog(LOG_ERR, "failed to claim AIO device: %s", libusb_error_name(ret));
return;
}
ret = libusb_control_transfer(dev, (LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE), 0x02, 0x02, 0, nullptr, 0, usb_timeout);
if (ret < 0)
applog(LOG_ERR, "AIO clear-to-send failed: %s", libusb_error_name(ret));
}
static void aio_usb_release(libusb_device_handle *dev) {
libusb_release_interface(dev, 0);
}
static int aio_post(libusb_device_handle *dev, const int req_len, const int resp_len) {
// applog(LOG_DBG, "writing to aio reg 0x%02x", req[0]);
int ret = 0;
ret += libusb_bulk_transfer(dev, 0x01, req, req_len, nullptr, usb_timeout);
ret += libusb_bulk_transfer(dev, 0x81, resp, resp_len, nullptr, usb_timeout);
return ret;
}
static float aio_get_water_temp(libusb_device_handle *dev) {
req[0] = 0xa9;
aio_post(dev, 1, 6);
return (float)resp[3] + (float)resp[4] / 10.0f;
}
static uint16_t aio_get_pump_speed(libusb_device_handle *dev) {
req[0] = 0x31;
if (aio_post(dev, 1, 5) != 0)
return UINT16_MAX;
return (uint16_t)(resp[3] << 8 | resp[4]);
}
static enum aio_pump_mode_t aio_get_pump_mode(libusb_device_handle *dev) {
req[0] = 0x33;
if (aio_post(dev, 1, 4) != 0)
return AIO_PUMP_MODE_UNKNOWN;
return (enum aio_pump_mode_t)resp[3];
}
static void aio_get_fan_speed(libusb_device_handle *dev, uint16_t *speeds) {
for (uint8_t i = 0; i < 2; i++) {
req[0] = 0x41;
req[1] = i;
if (aio_post(dev, 2, 6) != 0)
speeds[i] = UINT16_MAX;
speeds[i] = (uint16_t)(resp[4] << 8 | resp[5]);
}
}
static void aio_set_fan_duty(libusb_device_handle *dev, const uint8_t *duty) {
if (duty == nullptr)
return;
static uint8_t last_duty = 255;
if (duty[0] == last_duty)
return;
req[0] = 0x42;
for (uint8_t i = 0; i < 2; i++) {
req[1] = i;
req[2] = MIN(MAX(duty[i], 0), 100);
if (aio_post(dev, 3, buf_size) == 0)
last_duty = req[2];
}
}
static char *aio_get_firmware_version(libusb_device_handle *dev, char *buf) {
req[0] = 0xaa;
aio_post(dev, 1, buf_size);
sprintf(buf, "%d.%d.%d.%d", resp[3], resp[4], resp[5], resp[6]);
return buf;
}
static void aio_set_pump_mode(libusb_device_handle *dev, const enum aio_pump_mode_t mode) {
static enum aio_pump_mode_t last_mode = AIO_PUMP_MODE_UNKNOWN;
if (mode == last_mode)
return;
applog(LOG_DBG, "setting pump to %u", mode);
req[0] = 0x32;
req[1] = (uint8_t)mode;
if (aio_post(dev, 2, 6) == 0)
last_mode = mode;
}
static void aio_set_color(libusb_device_handle *dev, const enum aio_color_mode_t mode, const uint8_t speed, const struct rgb_t *colors, const uint8_t color_count) {
if (colors == nullptr) // NOLINT no its not
return;
constexpr uint8_t modes[] = {0x52, 0x55, 0x55, 0x58};
req[0] = 0x56;
req[1] = color_count;
// sort out and send color table
memcpy(&req[2], colors, color_count * 3);
if (color_count < 2 || mode == AIO_COLOR_FIXED) {
memcpy(&req[5], colors, 3); // copy the same color to the 2nd pos in the table
req[1] = 2;
}
aio_post(dev, 2 + req[1] * 3, buf_size);
// send speed, skip if fixed color
if (mode != AIO_COLOR_FIXED) {
constexpr uint8_t speed_shift[] = {0x46, 0x28, 0x0F};
constexpr uint8_t speed_pulse[] = {0x50, 0x37, 0x1E};
req[0] = 0x53;
req[1] = mode == AIO_COLOR_SHIFT ? speed_shift[speed] : speed_pulse[speed];
aio_post(dev,2, buf_size);
}
// actually set the aio to desired mode
req[0] = modes[mode];
req[1] = 0x01;
aio_post(dev, 2, buf_size);
}
static void aio_set_fan_curve(libusb_device_handle *dev, const struct curve_point_t *curve, const uint8_t count) {
if (curve == nullptr)
return;
if (count < 1 || count > max_curve_points) {
applog(LOG_ERR, "aio: curve must have 1-%d points", max_curve_points);
return;
}
struct curve_point_t padded[max_curve_points];
for (int i = 0; i < max_curve_points; i++)
padded[i] = (i < count) ? curve[i] : curve[count - 1];
req[0] = 0x40;
// setup request
for (int i = 0; i < max_curve_points; i++)
req[2 + i] = padded[i].temp;
for (int i = 0; i < max_curve_points; i++)
req[9 + i] = padded[i].duty;
// set curve for both fans
for (uint8_t i = 0; i < 2; i++) {
req[1] = i;
aio_post(dev, 16, sizeof(resp));
}
applog(LOG_INF, "set up AIO fan curve");
}
void aio_detect(struct aio_t *aio) {
libusb_init(nullptr);
aio->handle = libusb_open_device_with_vid_pid(nullptr, AIO_USB_VID, AIO_USB_PID);
if (aio->handle == nullptr) {
applog(LOG_ERR, "Can't open AIO USB device %x:%x", AIO_USB_VID, AIO_USB_PID);
return;
}
/* detaches other software that might take the device? */
if (libusb_kernel_driver_active(aio->handle, 0))
libusb_detach_kernel_driver(aio->handle, 0);
aio_usb_claim(aio->handle);
applog(LOG_INF, "found Corsair AIO device at %04x:%04x, fw ver: %s", AIO_USB_VID, AIO_USB_PID, aio_get_firmware_version(aio->handle, (char[16]){0}));
aio_set_fan_duty(aio->handle, nullptr); // so it doenst complain the func is unused
aio_set_fan_curve(aio->handle, fan_curve, sizeof(fan_curve) / sizeof(fan_curve[0]));
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_QUIET); // needed for init ig?
aio_set_color(aio->handle, AIO_COLOR_FIXED, 0, nullptr, 0); // so it doenst complain the func is unused
aio_usb_release(aio->handle);
}
void aio_update(struct aio_t *aio) {
if (aio->handle == nullptr)
return;
aio_usb_claim(aio->handle);
aio->water_temp = aio_get_water_temp(aio->handle);
aio->pump_speed = aio_get_pump_speed(aio->handle);
aio->pump_mode = aio_get_pump_mode(aio->handle);
aio_get_fan_speed(aio->handle, aio->fan_speed);
const uint16_t avg_rpm = (uint16_t)((aio->fan_speed[0] + aio->fan_speed[1]) / 2);
switch (aio->pump_mode) {
case AIO_PUMP_MODE_QUIET:
if (avg_rpm > 400)
aio->pump_counter++;
else
aio->pump_counter = 0;
if (aio->pump_counter >= 300) {
aio->pump_counter = 0;
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_BALANCED);
}
break;
case AIO_PUMP_MODE_BALANCED:
if (avg_rpm >= 700)
aio->pump_counter++;
else if (avg_rpm <= 375)
aio->pump_counter--;
else
aio->pump_counter = 0;
if (aio->pump_counter >= 300) {
aio->pump_counter = 0;
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_EXTREME);
}
if (aio->pump_counter <= -300) {
aio->pump_counter = 0;
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_QUIET);
}
break;
case AIO_PUMP_MODE_EXTREME:
if (avg_rpm <= 500)
aio->pump_counter--;
else
aio->pump_counter = 0;
if (aio->pump_counter <= -300) {
aio->pump_counter = 0;
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_BALANCED);
}
break;
case AIO_PUMP_MODE_UNKNOWN:
break;
}
aio_usb_release(aio->handle);
}
void aio_close(const struct aio_t *aio) {
aio_set_pump_mode(aio->handle, AIO_PUMP_MODE_BALANCED);
libusb_attach_kernel_driver(aio->handle, 0); // reattach so liquidctl still works
libusb_close(aio->handle);
libusb_exit(nullptr);
applog(LOG_INF, "AIO device closed");
}

View File

@@ -1,209 +0,0 @@
#include <LCDDisplay.hpp>
#include <app.hpp>
#include <chrono>
#include <cpu.hpp>
#include <csignal>
#include <functional>
#include <gpu.hpp>
#include <hass.hpp>
#include <log.hpp>
#include <mutex>
#include <stop_token>
#include <string>
#include <sys.hpp>
#include <thread>
#include <utility>
#include <utils.hpp>
#include <vector>
Application::Application(Config config) {
m_config = std::move(config);
// set up signals
sigemptyset(&m_control.sigset);
sigaddset(&m_control.sigset, SIGINT);
sigaddset(&m_control.sigset, SIGTERM);
applog(LOG::START, "Application initialized");
}
Application::~Application() {
auto level = LOG::START;
if (m_ret < 0) level = LOG::WRN;
applog(level, "Application destroyed");
}
int Application::Run() {
int sig;
m_mainPid = getpid();
pthread_sigmask(SIG_BLOCK, &m_control.sigset, nullptr);
{
std::vector<std::jthread> threads;
std::function<void(std::stop_token)> funcs[] = {
[this](const std::stop_token& st) { displayThread(st); },
[this](const std::stop_token& st) { sensorsThread(st); },
[this](const std::stop_token& st) { hassThread(st); },
};
// start threads in order
for (auto& f : funcs) {
threads.emplace_back(f);
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // start threads with delay so displayThread can quit the app if theres no lcd
}
// waits for signal, the threads are stopped when we go out of scope
sigwait(&m_control.sigset, &sig);
applog(LOG::WRN, "got stop signal, shutting down...");
}
return m_ret;
}
void Application::Crash(const int ret) {
m_ret = ret;
kill(m_mainPid, SIGINT);
}
void Application::hassThread(const std::stop_token& st) { //NOLINT
applog(LOG::START, "Thread started");
#ifdef ENABLE_HOME_ASSISTANT
static double power;
HomeAssistant::Config config;
config.address = m_config.hass.url;
config.entity = m_config.hass.entity;
config.token = m_config.hass.token;
HomeAssistant pc_power(config);
pc_power.connect();
while (!st.stop_requested()) {
pc_power.service();
if (power != pc_power.state) {
power = pc_power.state;
{
std::lock_guard lock(m_control.sensors);
m_sensors.hass_state = power;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
#else
(void)st;
applog(LOG::WRN, "feature disabled at compile time, probably no libwebsockets on host. exiting thread");
{
std::lock_guard lock(m_control.sensors);
m_sensors.hass_state = -1.0;
}
#endif
}
void Application::sensorsThread(const std::stop_token& st) {
applog(LOG::START, "Thread started, poll interval {} ms", m_config.lcd.refresh_interval_ms);
int ret = cpu::detect(&m_sensors.cpu, m_config.cpu_pciaddr);
ret += gpu::detect(&m_sensors.gpu, m_config.gpu_pciaddr);
ret += sys::detect(&m_sensors.sys, m_config.sys_sensor);
m_sensors.home.mountpoint = m_config.home_dir;
if (ret < 0)
applog(LOG::ERR, "Failed to detect some sensors");
while (!st.stop_requested()) {
auto start = std::chrono::steady_clock::now();
{
std::lock_guard lock(m_control.sensors);
sys::getTimeDate(&m_sensors.td);
getloadavg(m_sensors.loadavg, 3);
sys::getRam(&m_sensors.ram);
sys::getDisk(&m_sensors.home);
sys::getNetwork(&m_sensors.net, m_config.sys_ifname);
sys::updateSensors(&m_sensors.sys);
cpu::update(&m_sensors.cpu);
gpu::update(&m_sensors.gpu);
}
// log(LOG::DBG, "took {} microseconds", std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count());
m_control.display.release(); // unlocks display loop
std::this_thread::sleep_until(start + std::chrono::milliseconds(m_config.lcd.refresh_interval_ms));
}
applog(LOG::INF, "Thread stopped");
}
void Application::displayThread(const std::stop_token& st) {
std::stop_callback cb(st, [&] { m_control.display.release(); });
applog(LOG::START, "Thread started");
// init internal variables of LCDDisplay class
LCDDisplay lcd(m_config.lcd.port);
if (const int ret = lcd.Init(); ret < 0) {
applog(LOG::ERR, "Failed to initialize LCDDisplay");
this->Crash(ret);
return;
}
// basic lcd setup
lcd.setContrast(m_config.lcd.contrast);
lcd.setBacklight(true);
lcd.setCustomChar(0, {16, 6, 9, 8, 8, 9, 6, 0}); // celcius
lcd.setCustomChar(1, {0, 0, 4, 10, 10, 17, 31, 0}); // delta
lcd.setCustomChar(2, {0, 0, 8, 21, 21, 2, 0, 0}); // home (tilde)
lcd.setCustomChar(3, {4, 14, 21, 4, 4, 4, 0}); // up arrow
lcd.setCustomChar(4, {4, 4, 4, 21, 14, 4, 0}); // down arrow
// welcome msg
lcd.writeBuf(2, 0, utils::center("Waiting for sensors...", 40));
lcd.flushBuf();
lcd.clearBuf();
// actual main display loop
while (!st.stop_requested()) {
m_control.display.try_acquire_for(std::chrono::milliseconds(static_cast<int>(m_config.lcd.refresh_interval_ms * 1.25))); // holds the loop until sensor data is ready, max 1.25x of the loop time
if (st.stop_requested()) break; // needed, otherwise it wont quit correctly
{
std::lock_guard lock(m_control.sensors);
// prepare some tricky sensors
std::string gpu_temps = std::to_string(m_sensors.gpu.temp_edge);
if (m_sensors.gpu.temp_hotspot) {
const auto delta = m_sensors.gpu.temp_hotspot - m_sensors.gpu.temp_edge;
gpu_temps.append("/" + std::to_string(m_sensors.gpu.temp_hotspot));
gpu_temps.insert(0, LCDDisplay::cc(1) + std::to_string(delta) + LCDDisplay::cc(0) + " ");
}
uint16_t cpu_fan = 0;
for (const auto& fan : m_sensors.sys.fan_speed)
cpu_fan += fan;
cpu_fan /= 2;
// write to display buffer
lcd.writeBuf(0, 0, utils::pad(m_sensors.td.date, 11, utils::just::left) + " " + m_sensors.td.time + lcd.bar(7, m_sensors.gpu.usage_core / 100.0));
lcd.writeBuf(0, 26, utils::pNum(m_sensors.loadavg[0], m_sensors.loadavg[0] > 10 ? 1 : 2));
lcd.writeBuf(0, 31, utils::pNum(m_sensors.loadavg[1], m_sensors.loadavg[1] > 10 ? 1 : 2));
lcd.writeBuf(0, 36, utils::pNum(m_sensors.loadavg[2], m_sensors.loadavg[2] > 10 ? 1 : 2));
lcd.writeBuf(1, 0, utils::pad(std::to_string(m_sensors.cpu.tctl) + "/" + std::to_string(m_sensors.cpu.tccd1) + LCDDisplay::cc(0), 8, utils::just::left));
lcd.writeBuf(1, 7, utils::pad(utils::pBytes(m_sensors.ram.used, true), 6, utils::just::left));
if (m_sensors.hass_state >= 0)
lcd.writeBuf(1, 13, utils::pad(utils::pNum(m_sensors.hass_state) + "W", 4));
lcd.writeBuf(1, 19, std::to_string(m_sensors.sys.t_sensor) + LCDDisplay::cc(0)); // t_sensor temp
lcd.writeBuf(1, 32 - gpu_temps.size(), utils::pad(utils::pBytes(m_sensors.gpu.vram_used), 6));
lcd.writeBuf(1, 39 - gpu_temps.size(), gpu_temps + LCDDisplay::cc(0));
lcd.writeBuf(2, 0, utils::pad(std::to_string(static_cast<int>(m_sensors.cpu.ptotal)) + "W", 8, utils::just::left));
lcd.writeBuf(2, m_sensors.cpu.ptotal < 100 ? 4 : 5, std::to_string(cpu_fan) + "R"); // avg fan speed
lcd.writeBuf(2, 12, LCDDisplay::cc(3) + utils::pBytes(m_sensors.net.upload));
lcd.writeBuf(2, 19, LCDDisplay::cc(4) + utils::pBytes(m_sensors.net.download));
lcd.writeBuf(2, 26, utils::pad(std::to_string(m_sensors.gpu.fan_speed) + "R", 5));
lcd.writeBuf(2, 31, utils::pad(std::to_string(m_sensors.gpu.power_avg) + "/" + std::to_string(m_sensors.gpu.power_cap) + "W", 9));
lcd.writeBuf(3, 0, std::to_string(m_sensors.cpu.avg_freq) + "MHz");
lcd.writeBuf(3, 14, utils::pad(LCDDisplay::cc(2) + utils::pBytes(m_sensors.home.usage.free), 6));
lcd.writeBuf(3, 21, utils::pad(std::to_string(m_sensors.gpu.clock_core) + "/" + std::to_string(m_sensors.gpu.clock_mem) + "MHz ", 13));
lcd.writeBuf(3, 34, utils::pad(utils::pNum(m_sensors.gpu.vcore, 3) + "V", 5));
}
lcd.flushBuf();
lcd.clearBuf();
}
// goodbye msg
lcd.writeBuf(2, 0, utils::center("Goodbye", 40));
lcd.flushBuf();
}

105
src/cpu.c Normal file
View File

@@ -0,0 +1,105 @@
#include "cpu.h"
#include "log.h"
#include "utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
static sensor_file_t* sensors;
static sensor_file_t* cpu_freqs;
static constexpr uint64_t max_energy_range_uj = 65532610987;
void cpu_detect(struct cpu_sensors_t *t, const char *pci_dev) {
t->module = unknown;
t->num_cores = (uint8_t)sysconf(_SC_NPROCESSORS_ONLN);
snprintf(t->hwmon, sizeof(t->hwmon), "/sys/bus/pci/devices/%s/hwmon", pci_dev);
const int ret = get_hwmon_dir(t->hwmon, sizeof(t->hwmon));
if (ret != 0) {
t->hwmon[0] = '\0';
applog(LOG_WRN, "Detected %d cores, module enum #%d. Can't get hwmon path, skipping", t->num_cores, t->module);
return;
}
// fast_read_num_u64("/sys/class/powercap/intel-rapl:0/", "max_energy_range_uj", buf, &max_energy_range_uj, 1);
// t->module = read_file(t->hwmon, "/in1_input", nullptr, 0) ? zenpower : k10temp; // if we can read zenpower file then zenpower. idk if this is reliable, we're trying to avoid just reading the name
t->module = k10temp; // just assume its k10temp until we figure out a better way to detect it
applog(LOG_INF, "Detected %d cores, module enum #%d in path %s", t->num_cores, t->module, t->hwmon);
cpu_freqs = malloc(sizeof(sensor_file_t) * t->num_cores);
sensors = malloc(sizeof(sensor_file_t) * (t->module == zenpower ? 4 : 3));
for (uint8_t i = 0; i < t->num_cores; i++) {
char p[128];
sprintf(p, "/sys/devices/system/cpu/cpu%d/cpufreq/", i);
open_sensor(&cpu_freqs[i], p, "cpuinfo_avg_freq", nullptr, 1000, SENSOR_U64);
}
if (max_energy_range_uj != 0)
open_sensor(&sensors[0], "/sys/class/powercap/intel-rapl:0/", "energy_uj", nullptr, 1, SENSOR_U64);
if (t->module == k10temp) {
open_sensor(&sensors[1], t->hwmon, "/temp1_input", &t->tctl, 1000, SENSOR_I16);
open_sensor(&sensors[2], t->hwmon, "/temp3_input", &t->tccd1, 1000, SENSOR_I16);
} else if (t->module == zenpower) {
open_sensor(&sensors[1], t->hwmon, "/temp1_input", &t->tdie, 1000, SENSOR_I16);
open_sensor(&sensors[2], t->hwmon, "/temp2_input", &t->tctl, 1000, SENSOR_I16);
open_sensor(&sensors[3], t->hwmon, "/temp3_input", &t->tccd1, 1000, SENSOR_I16);
}
}
void cpu_update(struct cpu_sensors_t *c) {
struct energy_t {
struct timespec ts;
uint64_t energy;
};
static struct energy_t prev;
struct energy_t curr = {};
uint64_t energy_delta;
// read power from intel-rapl? works on amd too, btop uses this trick, seems accurate
// it tracks with "socket power" in hwinfo on windows, its actually the same thing i had in lcdsmartie
clock_gettime(CLOCK_MONOTONIC, &curr.ts);
fast_read_sensor_into_mem(&sensors[0], &curr.energy);
if (curr.energy >= prev.energy)
energy_delta = curr.energy - prev.energy;
else
energy_delta = (max_energy_range_uj - prev.energy) + curr.energy;
c->ptotal = (float)energy_delta / (float)timespec_diff_us(&curr.ts, &prev.ts);
if (c->ptotal > 500.0f || c->ptotal < 0.0f)
c->ptotal = 0.0f;
else
prev = curr;
// lots of reads but still faster than reading /proc/cpuinfo
c->avg_freq = 0;
for (uint8_t i = 0; i < c->num_cores; i++) {
uint64_t t = 0;
fast_read_sensor_into_mem(&cpu_freqs[i], &t);
c->avg_freq += (uint32_t)t;
}
c->avg_freq /= (c->num_cores > 1) ? c->num_cores : 1;
if (c->module == unknown)
return;
if (c->module == k10temp) {
fast_read_sensor(&sensors[1]);
fast_read_sensor(&sensors[2]);
}
// commented out unused values so its faster
if (c->module == zenpower) {
fast_read_sensor(&sensors[1]);
fast_read_sensor(&sensors[2]);
fast_read_sensor(&sensors[3]);
// utils::fastReadNum<1000>(c->hwmon + "/in1_input", buf, c->vcore);
// utils::fastReadNum<1000>(c->hwmon + "/in2_input", buf, c->vsoc);
}
}

View File

@@ -1,72 +0,0 @@
#include <array>
#include <chrono>
#include <cpu.hpp>
#include <cstdint>
#include <log.hpp>
#include <string>
#include <thread>
#include <utils.hpp>
namespace cpu {
int detect(cpuSensors* target, const std::string& pci) {
const std::string path = "/sys/bus/pci/devices/" + pci + "/hwmon";
try {
target->hwmon = utils::getFirstDir(path);
} catch (...) {
applog(LOG::ERR, "NO CPU HWMON KERNEL MODULE DETECTED");
return -1;
}
target->module = utils::fileExists(target->hwmon + "/in1_input") ? zenpower : k10temp; // idk if this is reliable, we're trying to avoid just reading the name
target->num_cores = std::thread::hardware_concurrency();
applog(LOG::INF, "Detected {} cores, module enum #{} in path \"{}\"", target->num_cores, static_cast<uint8_t>(target->module), target->hwmon);
return 0;
}
void update(cpuSensors* c) {
static std::array<char, 32> buf;
struct EnergyTs {
uint64_t energy = 0;
std::chrono::time_point<std::chrono::steady_clock> ts;
};
static EnergyTs prev;
EnergyTs curr;
// read power from intel-rapl? works on amd too, btop uses this trick, seems accurate
// it tracks with "socket power" in hwinfo on windows, its actually the same thing i had in lcdsmartie
curr.ts = std::chrono::steady_clock::now();
utils::fastReadNum("/sys/class/powercap/intel-rapl:0/energy_uj", buf, curr.energy);
c->ptotal = static_cast<float>(curr.energy - prev.energy) / static_cast<float>(std::chrono::duration_cast<std::chrono::microseconds>(curr.ts - prev.ts).count());
prev = curr;
// lots of reads but still faster than reading /proc/cpuinfo
c->avg_freq = 0;
for (uint8_t i = 0; i < c->num_cores; i++) {
uint32_t t = 0;
utils::fastReadNum<1000>("/sys/devices/system/cpu/cpu" + std::to_string(i) + "/cpufreq/cpuinfo_avg_freq", buf, t);
c->avg_freq += t;
}
c->avg_freq /= (c->num_cores > 1) ? c->num_cores : 1;
if (c->hwmon.empty())
return;
// commented out unused values so its faster
if (c->module == zenpower) {
utils::fastReadNum<1000>(c->hwmon + "/temp1_input", buf, c->tdie);
utils::fastReadNum<1000>(c->hwmon + "/temp2_input", buf, c->tctl);
utils::fastReadNum<1000>(c->hwmon + "/temp3_input", buf, c->tccd1);
// utils::fastReadNum<1000>(c->hwmon + "/in1_input", buf, c->vcore);
// utils::fastReadNum<1000>(c->hwmon + "/in2_input", buf, c->vsoc);
}
if (c->module == k10temp) {
utils::fastReadNum<1000>(c->hwmon + "/temp1_input", buf, c->tctl);
utils::fastReadNum<1000>(c->hwmon + "/temp3_input", buf, c->tccd1);
}
}
} // namespace cpu

136
src/gpu.c Normal file
View File

@@ -0,0 +1,136 @@
#include "gpu.h"
#include "gpu_metrics.h"
#include <stdio.h>
#include "log.h"
#include "utils.h"
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static constexpr char rel_metrics_path[] = "/../../gpu_metrics"; // gpu_metrics path relative to hwmon path
static constexpr uint16_t max_metrics_length = 512; // length of the metrics buffer
static sensor_file_t* sensors;
static int gpu_metrics_fd;
void gpu_detect(struct gpu_sensors_t *t, const char *pci) {
snprintf(t->hwmon, sizeof(t->hwmon), "/sys/bus/pci/devices/%s/hwmon", pci);
const int ret = get_hwmon_dir(t->hwmon, sizeof(t->hwmon));
if (ret != 0) {
t->hwmon[0] = '\0';
applog(LOG_WRN, "Detected GPU hwmon");
return;
}
t->has_gpu_metrics = false;
char p[160];
char *nt = stpcpy(p, t->hwmon);
strcpy(nt, rel_metrics_path);
gpu_metrics_fd = open(p, O_RDONLY | O_CLOEXEC);
if (gpu_metrics_fd > 0) {
t->has_gpu_metrics = true;
char raw_metrics[max_metrics_length];
read(gpu_metrics_fd, raw_metrics, sizeof(raw_metrics));
const struct metrics_table_header *header = (struct metrics_table_header *)raw_metrics;
applog(LOG_INF, "Found gpu_metrics_v%d_%d", header->format_revision, header->content_revision);
}
sensors = malloc(sizeof(sensor_file_t) * (t->has_gpu_metrics ? 10 : 5));
// we always open these
open_sensor(&sensors[0], t->hwmon, "/in0_input", &t->vcore, 1000, SENSOR_FLOAT);
open_sensor(&sensors[1], t->hwmon, "/fan1_input", &t->fan_speed, 1, SENSOR_U16);
open_sensor(&sensors[2], t->hwmon, "/power1_cap", &t->power_cap, 1000000, SENSOR_U16);
open_sensor(&sensors[3], t->hwmon, "/power1_average", &t->power_avg, 1000000, SENSOR_U16);
open_sensor(&sensors[4], t->hwmon, "/../../mem_info_vram_used", &t->vram_used, 1, SENSOR_U64);
if (t->has_gpu_metrics == false) {
open_sensor(&sensors[5], t->hwmon, "/freq1_input", &t->clock_core, 1000000, SENSOR_U16);
open_sensor(&sensors[6], t->hwmon, "/freq2_input", &t->clock_mem, 1000000, SENSOR_U16);
open_sensor(&sensors[7], t->hwmon, "/power1_input",&t->power_avg, 1000000, SENSOR_U16);
open_sensor(&sensors[8], t->hwmon, "/temp1_input", &t->temp_edge, 1000, SENSOR_U8);
open_sensor(&sensors[9], t->hwmon, "/../../gpu_busy_percent", &t->usage_core, 1, SENSOR_U8);
}
}
/* some metrics are missing in gpu_metrics, we always fill them from fs, unused metrics are commented out */
static void fill_missing_metrics() {
// g->fan_pwm = std::stoi(readFile(g->hwmon + "/pwm1", 4)) * 100 / 256;
// utils::fastReadNum(g->hwmon + "/power1_cap_max", g->power_cap_max) / 1000000;
// utils::fastReadNum(g->hwmon + "/../../mem_info_vram_total", g->vram_total);
for (uint8_t i = 0; i < 5; i++)
fast_read_sensor(&sensors[i]);
}
/* get sensors for pre-vega cards (rx480/580) */
static void update_from_fs() {
for (uint8_t i = 5; i < 10; i++)
fast_read_sensor(&sensors[i]);
}
static void update_from_metrics(struct gpu_sensors_t *g) {
char raw_metrics[max_metrics_length];
lseek(gpu_metrics_fd, 0, SEEK_SET);
read(gpu_metrics_fd, raw_metrics, sizeof(raw_metrics));
const struct metrics_table_header *header = (struct metrics_table_header *)raw_metrics;
switch (header->format_revision * 10 + header->content_revision) {
/* rx5700xt, rx7800xt, rx9070xt */
case 13: {
const struct gpu_metrics_v1_3 *metrics = (struct gpu_metrics_v1_3 *)raw_metrics;
g->usage_core = (uint8_t)metrics->average_gfx_activity;
g->usage_umc = (uint8_t)metrics->average_umc_activity;
g->usage_media = (uint8_t)metrics->average_mm_activity;
g->temp_edge = (uint8_t)metrics->temperature_edge;
g->temp_hotspot = (uint8_t)metrics->temperature_hotspot;
g->temp_mem = (uint8_t)metrics->temperature_mem;
g->temp_vrm_gfx = (uint8_t)metrics->temperature_vrgfx;
g->temp_vrm_soc = (uint8_t)metrics->temperature_vrsoc;
g->temp_vrm_mem = (uint8_t)metrics->temperature_vrmem;
// g->power_avg = metrics->average_socket_power; // jumpy on 9070xt
g->fan_speed = metrics->current_fan_speed;
g->clock_core = metrics->average_gfxclk_frequency;
g->clock_mem = metrics->average_uclk_frequency; // mem controller clock, needs to be 2x on 5700xt for some reason
// hacks for rx7800xt
// g->clock_core = metrics->average_vclk1_frequency; // why not gfxclock?
// g->clock_mem = metrics->average_gfxclk_frequency; // why gfxclock? min mem clock seems to be 96mhz, this is probably including time sleeping
break;
}
/* ryzen 5 5600g */
case 22: {
const struct gpu_metrics_v2_2 *metrics = (struct gpu_metrics_v2_2 *)raw_metrics;
g->temp_edge = (uint8_t)(metrics->temperature_gfx / 100);
break;
}
/* werid hardware i probably dont like, maybe fallback to reading from fs? */
default: {
applog(LOG_WRN, "Unsupported gpu_metrics_v%d_%d", header->format_revision, header->content_revision);
break;
}
}
}
/* reads values from system and updates the target gpu_sensors_t struct */
void gpu_update(struct gpu_sensors_t *g) {
const bool hwmon_valid = g->hwmon[0] != '\0';
if (hwmon_valid)
fill_missing_metrics();
if (g->has_gpu_metrics)
update_from_metrics(g);
else if (hwmon_valid)
update_from_fs();
if (g->fan_speed > 9999)
g->fan_speed = 9999; // sanity check fan speed
}

View File

@@ -1,123 +0,0 @@
#include <array>
#include <gpu.hpp>
#include <gpu_metrics.hpp>
#include <log.hpp>
#include <string>
#include <utils.hpp>
/* maybe have a read pp_table function */
/* would be neat to wrap it in a class but eh namespace is good enough */
namespace gpu {
const std::string rel_metrics_path = "/../../gpu_metrics"; // gpu_metrics path relative to hwmon path
constexpr uint16_t max_metrics_length = 512; // length of the metrics buffer
/* takes pointer to a target gpu::sensors struct and updates its hwmon path and detects gpu_metrics */
int detect(gpuSensors* target, const std::string& pci) {
try {
target->hwmon = utils::getFirstDir("/sys/bus/pci/devices/" + pci + "/hwmon");
} catch (...) {
applog(LOG::ERR, "Couldn't find GPU hwmon");
return -1;
}
applog(LOG::INF, "Detected path \"{}\"", target->hwmon);
if (utils::fileExists(target->hwmon + rel_metrics_path)) {
std::array<char, max_metrics_length> raw_metrics{};
if (!utils::readFile(target->hwmon + rel_metrics_path, raw_metrics))
return -1;
const auto header = reinterpret_cast<metrics::metrics_table_header*>(raw_metrics.data());
applog(LOG::INF, "Found gpu_metrics_v{}_{}", header->format_revision, header->content_revision);
target->hasGpuMetrics = true;
}
return 0;
}
/* some metrics are missing in gpu_metrics, we always fill them from fs, unused metrics are commented out */
void fillMissingMetrics(gpuSensors* g) {
static std::array<char, 32> buf;
utils::fastReadNum<1000>(g->hwmon + "/in0_input", buf, g->vcore);
// g->fan_pwm = std::stoi(readFile(g->hwmon + "/pwm1", 4)) * 100 / 256;
utils::fastReadNum(g->hwmon + "/fan1_input", buf, g->fan_speed);
utils::fastReadNum<1000000>(g->hwmon + "/power1_cap", buf, g->power_cap);
// utils::fastReadNum(g->hwmon + "/power1_cap_max", g->power_cap_max) / 1000000;
utils::fastReadNum(g->hwmon + "/../../mem_info_vram_used", buf, g->vram_used);
// utils::fastReadNum(g->hwmon + "/../../mem_info_vram_total", g->vram_total);
}
/* get sensors for pre-vega cards (rx480/580) */
void updateFromFs(gpuSensors* g) {
static std::array<char, 32> buf;
utils::fastReadNum<1000000>(g->hwmon + "/freq1_input", buf, g->clock_core);
utils::fastReadNum<1000000>(g->hwmon + "/freq2_input", buf, g->clock_mem);
utils::fastReadNum<1000000>(g->hwmon + "/power1_input", buf, g->power_avg);
utils::fastReadNum<1000>(g->hwmon + "/temp1_input", buf, g->temp_edge);
utils::fastReadNum(g->hwmon + "/../../gpu_busy_percent", buf, g->usage_core);
}
/* get sensors for vega and newer (rx5700xt, rx7800xt [*]) */
void updateFromMetrics(gpuSensors* g) {
std::array<char, max_metrics_length> raw_metrics{};
if (!utils::readFile(g->hwmon + rel_metrics_path, raw_metrics)) return;
switch (const auto header = reinterpret_cast<metrics::metrics_table_header*>(raw_metrics.data()); header->format_revision * 10 + header->content_revision) {
/* rx5700xt, rx7800xt */
case 13: {
const auto metrics = reinterpret_cast<metrics::gpu_metrics_v1_3*>(raw_metrics.data());
g->usage_core = metrics->average_gfx_activity;
g->usage_umc = metrics->average_umc_activity;
g->usage_media = metrics->average_mm_activity;
g->temp_edge = metrics->temperature_edge;
g->temp_hotspot = metrics->temperature_hotspot;
g->temp_mem = metrics->temperature_mem;
g->temp_vrm_gfx = metrics->temperature_vrgfx;
g->temp_vrm_soc = metrics->temperature_vrsoc;
g->temp_vrm_mem = metrics->temperature_vrmem;
g->power_avg = metrics->average_socket_power;
g->fan_speed = metrics->current_fan_speed;
// hacks for rx7800xt
// g->clock_core = metrics->average_vclk1_frequency; // why not gfxclock?
// g->clock_mem = metrics->average_gfxclk_frequency; // why gfxclock? min mem clock seems to be 96mhz, this is probably including time sleeping
// hacks for rx5700xt
g->clock_core = metrics->average_gfxclk_frequency;
g->clock_mem = metrics->average_uclk_frequency * 2; // mem controller clock, actual mem is 2x that
break;
}
/* ryzen 5 5600g */
case 22: {
const auto metrics = reinterpret_cast<metrics::gpu_metrics_v2_2*>(raw_metrics.data());
g->temp_edge = metrics->temperature_gfx / 100;
break;
}
/* werid hardware i probably dont like, maybe fallback to reading from fs? */
default: {
applog(LOG::WRN, "Unsupported gpu_metrics_v{}_{}", header->format_revision, header->content_revision);
break;
}
}
}
/* reads values from system and updates the target gpu::sensors struct */
void update(gpuSensors* g) {
const bool hwmon_valid = !g->hwmon.empty();
if (hwmon_valid)
fillMissingMetrics(g);
if (g->hasGpuMetrics)
updateFromMetrics(g);
else
if (hwmon_valid)
updateFromFs(g);
if (g->fan_speed > 9999) g->fan_speed = 9999; // sanity check fan speed
}
} // namespace gpu

View File

@@ -1,163 +0,0 @@
#ifdef ENABLE_HOME_ASSISTANT
#include <libwebsockets.h>
#include <cstring>
#include <hass.hpp>
#include <log.hpp>
#include <nlohmann/json.hpp>
#include <string>
HomeAssistant::HomeAssistant(const Config& config) {
m_config = config;
}
HomeAssistant::~HomeAssistant() {
disconnect();
}
void HomeAssistant::event_handler(lws* wsi, const lws_callback_reasons reason, char* in) {
switch (reason) {
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: {
applog(LOG::ERR, "websocket conn error: {}", in ? in : "(null)");
return;
}
case LWS_CALLBACK_CLIENT_RECEIVE: {
nlohmann::json json = nlohmann::json::parse(in);
const std::string ha_type = json["type"].get<std::string>();
if (ha_type == "auth_required") {
json.clear();
json["type"] = "auth";
json["access_token"] = m_config.token;
tx = json.dump();
break;
}
if (ha_type == "auth_ok") {
applog(LOG::INF, "subscribing to entity \"{}\"", m_config.entity);
json.clear();
json["id"] = id++;
json["type"] = "subscribe_entities";
json["entity_ids"][0] = m_config.entity.c_str();
tx = json.dump();
break;
}
if (ha_type == "result") {
if (json["success"].get<bool>() != true)
applog(LOG::ERR, "api said success was \"false\" on message id {}", id - 1);
break;
}
if (ha_type == "event") {
json = json["event"];
if (json.contains("a"))
state = std::stof(json["a"][m_config.entity]["s"].get<std::string>());
if (json.contains("c"))
state = std::stof(json["c"][m_config.entity]["+"]["s"].get<std::string>());
break;
}
applog(LOG::WRN, "unhandled event: {}", static_cast<char*>(in));
break;
}
case LWS_CALLBACK_CLIENT_ESTABLISHED: {
applog(LOG::INF, "Websocket connected");
break;
}
case LWS_CALLBACK_CLIENT_CLOSED: {
applog(LOG::INF, "Websocket closed");
return;
}
case LWS_CALLBACK_CLIENT_WRITEABLE: {
/* skip whole callback if there's no tx to send */
if (!tx.empty()) {
const size_t msgLen = LWS_PRE + tx.size();
auto* buf = static_cast<unsigned char*>(malloc(msgLen));
if (buf) {
memcpy(&buf[LWS_PRE], tx.c_str(), tx.size());
lws_write(wsi, &buf[LWS_PRE], tx.size(), LWS_WRITE_TEXT);
tx = "";
free(buf);
}
}
break;
}
default:
break;
}
}
int HomeAssistant::callback(lws* wsi, const lws_callback_reasons reason, void* user, void* in, size_t) {
auto* self = static_cast<HomeAssistant*>(user);
self->event_handler(wsi, reason, static_cast<char*>(in));
return 0;
}
int HomeAssistant::connect() {
lws_client_connect_info i{};
lws_context_creation_info info{};
lws_set_log_level(LLL_ERR | LLL_WARN, lwsl_emit_syslog);
std::memset(&i, 0, sizeof(i));
std::memset(&info, 0, sizeof info);
static constexpr lws_protocols protocols[] = {
{"lws-minimal-client", callback, sizeof(void*), 0, 0, nullptr, 0},
LWS_PROTOCOL_LIST_TERM};
static constexpr lws_extension extensions[] = {
{"permessage-deflate",
lws_extension_callback_pm_deflate,
"permessage-deflate"
"; client_no_context_takeover"
"; client_max_window_bits"},
{nullptr, nullptr, nullptr}};
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.port = CONTEXT_PORT_NO_LISTEN;
info.protocols = protocols;
info.fd_limit_per_thread = 1 + 1 + 1;
info.extensions = extensions;
context = lws_create_context(&info);
if (!context) {
applog(LOG::ERR, "error creating context");
return -1;
}
i.context = context;
i.port = 443;
i.address = m_config.address.c_str();
i.path = "/api/websocket";
i.host = i.address;
i.origin = i.address;
i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_PRIORITIZE_READS;
i.protocol = nullptr;
i.userdata = this;
i.local_protocol_name = "lws-minimal-client";
websock = lws_client_connect_via_info(&i);
if (!websock) {
applog(LOG::ERR, "error creating lws client");
return -1;
}
return 0;
}
void HomeAssistant::service(const int timeout_ms) const {
lws_callback_on_writable(websock);
lws_service(context, timeout_ms);
}
void HomeAssistant::disconnect() const {
lws_context_destroy(context);
}
#endif

141
src/lcd.c Normal file
View File

@@ -0,0 +1,141 @@
#include "lcd.h"
#include "log.h"
#include <fcntl.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static int lcd_open(struct lcd_t *lcd, const bool reopen) {
applog(LOG_INF, "Opening serial port at %s", lcd->port);
if (lcd->fd <= 0 || reopen)
lcd->fd = open(lcd->port, O_WRONLY | O_NOCTTY);
if (lcd->fd <= 0) {
applog(LOG_ERR, "Error opening serial port %s", lcd->port); // err
return -1;
}
struct termios term = {};
term.c_cflag = lcd->baud | CS8 | CLOCAL | CREAD;
tcflush(lcd->fd, TCIFLUSH);
tcsetattr(lcd->fd, TCSANOW, &term);
return 0;
}
void lcd_close(struct lcd_t *lcd) {
if (lcd->fd >= 0) {
close(lcd->fd);
applog(LOG_INF, "LCD port closed");
}
lcd->fd = -1;
}
static void lcd_write(struct lcd_t *lcd, const uint8_t *cmdbuf, const uint16_t len) {
if (write(lcd->fd, cmdbuf, len) <= 0) {
applog(LOG_ERR, "failed to execute write(), lost access to port?");
lcd_close(lcd);
lcd_open(lcd, true);
}
}
void lcd_clear(struct lcd_t *lcd) {
constexpr uint8_t cmdbuf[] = {254, 88};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
void lcd_set_contrast(struct lcd_t *lcd, const uint8_t contrast) {
applog(LOG_INF, "Set contrast to %d", contrast);
const uint8_t cmdbuf[] = {254, 80, contrast};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
void lcd_set_backlight(struct lcd_t *lcd, const bool enabled) {
applog(LOG_INF, "Set backlight to %s", enabled ? "true" : "false");
const uint8_t cmdbuf[] = {254, (enabled ? 66 : 70), 255};
lcd_write(lcd, cmdbuf, enabled ? 3 : 2);
}
void lcd_set_cursor(struct lcd_t *lcd, const uint8_t row, const uint8_t col) {
const uint8_t cmdbuf[] = {254, 71, col + 1, row + 1};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
}
void lcd_set_custom_char(struct lcd_t *lcd, const uint8_t index, const lcd_glyph_t *glyph) {
memcpy(lcd->writebuf.cchar[index] + 3, glyph, sizeof(lcd_glyph_t)); // we prepare the command in init, so we can just copy the glyph after the command
}
/* writes string to write buffer at calculated pos, we trust it to be null terminated */
void lcd_write_buf(struct lcd_t *lcd, const uint8_t row, const uint8_t col, const char *str) {
size_t len = 0;
while (str[len] != '\0')
len++;
len = MIN(len, sizeof(lcd->writebuf) - col);
int pos = 4 + col + (LCD_COLS * (row % 2)); // skip 4 bytes to leave room for set cursor command
if (row >= 2)
pos += 2 * LCD_COLS + 4; // if we're writing to disp2, skip another 4 bytes for set cursor command and 2*40 bytes of disp 1
memcpy(lcd->writebuf.text + pos, str, len);
}
/* hyperoptimized function to write a formatted string to lcd buffer. */
/* we cant just snprintf to the buffer directly bc it null terminates */
void lcd_writef(struct lcd_t *lcd, const uint8_t x, const uint8_t y, const char *fmt, ...) {
char tmp[LCD_COLS + 1];
va_list ap;
va_start(ap, fmt);
int len = vsnprintf(tmp, sizeof(tmp), fmt, ap); // print fmt and ap to a temporary buffer
va_end(ap);
len = MIN(len, LCD_COLS - x);
int pos = 4 + x + (LCD_COLS * (y % 2)); // skip 4 bytes to leave room for set cursor command
if (y >= 2)
pos += 2 * LCD_COLS + 4; // if we're writing to disp2, skip another 4 bytes for set cursor command and 2*40 bytes of disp 1
memcpy(lcd->writebuf.text + pos, tmp, (size_t)len); // copy to lcd writebuffer address + whatever pos is
}
inline void lcd_clear_buf(struct lcd_t *lcd) {
memset(lcd->writebuf.text, ' ', sizeof(lcd->writebuf.text));
// prepare commands for custom chars
for (uint8_t i = 0; i < 8; i++) {
const uint8_t charbuf[] = {254, 78, i};
memcpy(lcd->writebuf.cchar[i], charbuf, sizeof(charbuf));
}
}
/* flushes writebuffer to lcd, hyperoptmized, flushes buffer and commands in a single write operation */
void lcd_flush_buf(struct lcd_t *lcd) {
constexpr uint8_t set_disp[] = {254, 71, 1, 1}; // set cursor to char 1, line 1
memcpy(&lcd->writebuf.text[0], set_disp, 4);
memcpy(&lcd->writebuf.text[2 * LCD_COLS + 4], set_disp, 4);
lcd->writebuf.text[2 * LCD_COLS + 7] = 3; // overwrite set cursor c1 l1 to c1 l3
lcd_write(lcd, (const uint8_t *)&lcd->writebuf, sizeof(lcd->writebuf));
}
// i have a whole flushbuf func to avoid too many writes, but im writing on every bar, need to rethink this.
// we could have a separate pre-char buffer and put all the setCustomChar commands to that?
inline char lcd_bar(struct lcd_t *lcd, const uint8_t char_id, const double value) {
uint8_t b[8];
memset(b, 0b01110, sizeof(b));
for (uint8_t i = 0; i < (1.0 - value) * 8; i++)
b[i] = 0;
lcd_set_custom_char(lcd, char_id, &b);
return (char)char_id;
}
int lcd_init(struct lcd_t *lcd) {
const int ret = lcd_open(lcd, false);
if (ret < 0) {
lcd_close(lcd);
return -1;
}
lcd_clear_buf(lcd);
constexpr uint8_t cmdbuf[] = {254, 75, 254, 84};
lcd_write(lcd, cmdbuf, sizeof(cmdbuf));
lcd_clear(lcd);
return 0;
}

73
src/log.c Normal file
View File

@@ -0,0 +1,73 @@
#include "log.h"
#ifdef APPLOG_ENABLED
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
static pthread_mutex_t cmtx = PTHREAD_MUTEX_INITIALIZER;
/* Replicates pFname: strips return type and parameters from __PRETTY_FUNCTION__
* "void myFunc(int x)" → "myFunc"
* "int other_func(void)" → "other_func"
*/
static void fmt_funcname(const char *pretty, char *out, size_t outsz) {
const char *start = pretty;
const char *space = strchr(pretty, ' ');
if (space)
start = space + 1;
const char *paren = strrchr(start, '(');
size_t len = paren ? (size_t)(paren - start) : strlen(start);
if (len >= outsz)
len = outsz - 1;
memcpy(out, start, len);
out[len] = '\0';
}
void log_write(const log_level_t level, const char *func, const char *fmt, ...) {
static const char *reset = "\033[0m";
const char *color;
switch (level) {
case LOG_INF:
color = "\033[1;32m";
break; /* bold green */
case LOG_WRN:
color = "\033[1;33m";
break; /* bold yellow */
case LOG_ERR:
color = "\033[1;31m";
break; /* bold red */
case LOG_DBG:
color = "";
break;
case LOG_START:
color = "\033[1;3;36m";
break; /* bold italic cyan */
default:
color = "";
break;
}
char funcname[128];
fmt_funcname(func, funcname, sizeof(funcname));
/* ── console ─────────────────────────────────────────────────────── */
{
va_list ap;
va_start(ap, fmt);
pthread_mutex_lock(&cmtx);
fprintf(stderr, "[%s%s%s] ", color, funcname, reset);
vfprintf(stderr, fmt, ap);
fputc('\n', stderr);
pthread_mutex_unlock(&cmtx);
va_end(ap);
}
}
#endif /* APPLOG_ENABLED */

239
src/main.c Normal file
View File

@@ -0,0 +1,239 @@
#include <pthread.h>
#include <signal.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/signalfd.h>
#include <unistd.h>
#include "aio.h"
#include "cpu.h"
#include "gpu.h"
#include "lcd.h"
#include "log.h"
#include "sys.h"
#include "utils.h"
#include "version.h"
static constexpr uint8_t lcd_contrast = 250;
static constexpr uint16_t refresh_interval_ms = 1000;
static constexpr char netif[] = "enp7s0";
static constexpr char cpu_pcidev[] = "0000:00:18.3";
static constexpr char gpu_pcidev[] = "0000:0a:00.0";
static struct {
pthread_mutex_t sensors_mtx;
pthread_cond_t sensors_ready;
atomic_bool running;
} control;
struct sensors_t { // NOLINT stfu
time_t ts;
struct memory_t ram;
struct disk_t home;
struct disk_t gry;
struct network_t net;
struct syssensors_t sys;
struct cpu_sensors_t cpu;
struct gpu_sensors_t gpu;
struct aio_t aio;
double loadavg[3];
};
static void *sensors_thread(void *arg) {
applog(LOG_START, "sensors thread started, refresh interval is %dms", refresh_interval_ms);
struct sensors_t *sensors = arg;
struct timespec next;
clock_gettime(CLOCK_REALTIME, &next);
/* set up paths for the sensors */
strcpy(sensors->home.mountpoint, "/home/drath");
strcpy(sensors->gry.mountpoint, "/var/mnt/gry");
strcpy(sensors->net.device, netif);
cpu_detect(&sensors->cpu, cpu_pcidev);
gpu_detect(&sensors->gpu, gpu_pcidev);
sys_detect(&sensors->sys, "nct6775.656");
sys_netdev_detect(&sensors->net);
aio_detect(&sensors->aio);
/* main sensor loop */
applog(LOG_START, "reached sensors main loop");
while (control.running) { // NOLINT
pthread_mutex_lock(&control.sensors_mtx);
{
sensors->ts = time(nullptr);
sys_get_load_avg(sensors->loadavg);
sys_get_ram(&sensors->ram);
sys_get_disk(&sensors->home);
sys_get_disk(&sensors->gry);
sys_get_network(&sensors->net);
sys_update_sensors(&sensors->sys);
cpu_update(&sensors->cpu);
gpu_update(&sensors->gpu);
aio_update(&sensors->aio);
}
pthread_cond_signal(&control.sensors_ready);
pthread_mutex_unlock(&control.sensors_mtx);
timespec_add_ms(&next, refresh_interval_ms);
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, nullptr);
}
aio_close(&sensors->aio);
applog(LOG_WRN, "sensors thread stopped");
return nullptr;
}
static void *display_thread(void *arg) {
applog(LOG_START, "display thread started");
const struct sensors_t *sensors = arg;
/* lcd setup */
struct lcd_t lcd = {
.port = "/dev/serial/by-id/usb-Raspberry_Pi_Pico_E6616407E37AA12F-if00",
.baud = 0010002, // from termios-baud.h, B115200
};
lcd_init(&lcd);
lcd_set_backlight(&lcd, true);
lcd_set_contrast(&lcd, lcd_contrast);
char name[64], ver[64];
sys_get_distro(name, ver);
lcd_writef(&lcd, 0, 0, "%s %s", name, ver);
lcd_writef(&lcd, 0, 3, "linuxSmartie %s", GIT_HASH);
lcd_flush_buf(&lcd);
lcd_clear_buf(&lcd);
struct timespec next;
clock_gettime(CLOCK_REALTIME, &next);
timespec_add_ms(&next, refresh_interval_ms);
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, nullptr);
/* setup custom chars */
lcd_set_custom_char(&lcd, 0, &(lcd_glyph_t){16, 6, 9, 8, 8, 9, 6, 0}); // celcius
lcd_set_custom_char(&lcd, 1, &(lcd_glyph_t){0, 0, 4, 10, 10, 17, 31, 0}); // delta
lcd_set_custom_char(&lcd, 2, &(lcd_glyph_t){0, 0, 8, 21, 21, 2, 0, 0}); // home (tilde)
lcd_set_custom_char(&lcd, 3, &(lcd_glyph_t){4, 14, 21, 4, 4, 4, 0}); // up arrow
lcd_set_custom_char(&lcd, 4, &(lcd_glyph_t){4, 4, 4, 21, 14, 4, 0}); // down arrow
lcd_set_custom_char(&lcd, 5, &(lcd_glyph_t){0, 0, 15, 21, 21, 17, 15, 0}); // games (steam?)
/* main display loop */
applog(LOG_START, "reached display main loop");
while (control.running) { // NOLINT its updated globally bud
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
timespec_add_ms(&ts, (uint16_t)(refresh_interval_ms * 1.25));
pthread_mutex_lock(&control.sensors_mtx); // lock sensor struct
pthread_cond_timedwait(&control.sensors_ready, &control.sensors_mtx, &ts); // wait until data ready
{
// prepare some tricky sensors
constexpr char modes[] = "QBE";
const uint16_t avg_fan_speed = (uint16_t)((sensors->aio.fan_speed[0] + sensors->aio.fan_speed[1]) / 2);
char gpudelta[5];
const uint8_t delta = sensors->gpu.temp_hotspot - sensors->gpu.temp_edge;
snprintf(gpudelta, sizeof(gpudelta), "%c%d", 1, delta);
const uint8_t gpu_pos = delta >= 10 ? 3 : 2;
char gpuclock[16];
snprintf(gpuclock, sizeof(gpuclock), "%d/%dMHz", sensors->gpu.clock_core, sensors->gpu.clock_mem);
lcd_writef(&lcd, 0, 0, "%c%s", 2, P_BYTES(sensors->home.usage.free, 1));
lcd_writef(&lcd, 7, 0, "%c%s", 5, P_BYTES(sensors->gry.usage.free, 1));
lcd_writef(&lcd, 15, 0, "%s%c", SYS_GET_TIME_STR(sensors->ts), lcd_bar(&lcd, 6, sensors->gpu.usage_core / 100.0));
lcd_writef(&lcd, 26, 0, "%s %s %s", P_NUM(sensors->loadavg[0]), P_NUM(sensors->loadavg[1]), P_NUM(sensors->loadavg[2]));
lcd_writef(&lcd, 0, 1, "%d/%d%c", sensors->cpu.tctl, sensors->cpu.tccd1, 0);
lcd_writef(&lcd, 7, 1, "%s", P_BYTES(sensors->ram.used, 0));
lcd_writef(&lcd, 13, 1, "%.1f%c", (double)sensors->aio.water_temp, 0);
lcd_writef(&lcd, 18, 1, "%3d%c", sensors->sys.t_sensor, 0);
lcd_writef(&lcd, 28, 1, "%4s%c", gpudelta, 0);
lcd_writef(&lcd, 25 - gpu_pos, 1, "%6s", P_BYTES(sensors->gpu.vram_used, 1));
lcd_writef(&lcd, 34, 1, "%d/%d%c", sensors->gpu.temp_edge, sensors->gpu.temp_hotspot, 0);
lcd_writef(&lcd, 0, 2, "%.0fW", (double)sensors->cpu.ptotal);
lcd_writef(&lcd, 5, 2, "%dR", avg_fan_speed);
lcd_writef(&lcd, 12, 2, "%5s%c", P_BYTES(sensors->net.upload, 1), 3);
lcd_writef(&lcd, 19, 2, "%c%s", 4, P_BYTES(sensors->net.download, 1));
lcd_writef(&lcd, 26, 2, "%4dR", sensors->gpu.fan_speed);
lcd_writef(&lcd, 32, 2, "%3d/%3dW", sensors->gpu.power_avg, sensors->gpu.power_cap);
lcd_writef(&lcd, 0, 3, "%uMHz", sensors->cpu.avg_freq);
lcd_writef(&lcd, 8, 3, "%4dR-%c %d", sensors->aio.pump_speed, modes[sensors->aio.pump_mode], sensors->aio.pump_counter);
lcd_writef(&lcd, 21, 3, "%12s", gpuclock);
lcd_writef(&lcd, 34, 3, "%.3fV", (double)sensors->gpu.vcore);
}
pthread_mutex_unlock(&control.sensors_mtx); // unlock sensor struct
lcd_flush_buf(&lcd);
lcd_clear_buf(&lcd);
}
lcd_write_buf(&lcd, 2, 16, "Goodbye"); // center
lcd_set_backlight(&lcd, false);
lcd_flush_buf(&lcd);
lcd_close(&lcd);
applog(LOG_WRN, "display thread stopped");
return nullptr;
}
int main() {
applog(LOG_START, "linuxSmartie %s starting", GIT_HASH);
applog(LOG_DBG, "Compiled with %s", C_STD);
/* see if another instance is up, quit if it is */
if (get_instance_lock() < 0) {
applog(LOG_ERR, "Another instance is already running. Exiting.");
return 0;
}
/* malloc memory for the struct */
struct sensors_t *sensors = malloc(sizeof(struct sensors_t)); // no actual point in mallocing but its cool
if (sensors == nullptr) {
applog(LOG_ERR, "can't allocate mem for the sensor struct. You don't got %lu bytes bro?", sizeof(*sensors));
free(sensors);
return 1;
}
applog(LOG_INF, "sensor struct allocated, size is %lu bytes", sizeof(*sensors));
/* signal setup */
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
sigaddset(&sigset, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
struct signalfd_siginfo info;
const int sfd = signalfd(-1, &sigset, SFD_CLOEXEC);
/* control vars setup */
pthread_mutex_init(&control.sensors_mtx, nullptr);
pthread_cond_init(&control.sensors_ready, nullptr);
control.running = true;
/* thread setup */
pthread_t sensors_pthread;
pthread_t display_pthread;
/* actually start the threads */
pthread_create(&sensors_pthread, nullptr, sensors_thread, sensors);
pthread_create(&display_pthread, nullptr, display_thread, sensors);
/* halts here until signal to stop */
const ssize_t ret = read(sfd, &info, sizeof(info));
(void)ret;
/* cleanup on exit */
applog(LOG_WRN, "got stop signal, shutting down...");
close(sfd);
control.running = false;
pthread_join(sensors_pthread, nullptr);
pthread_join(display_pthread, nullptr);
free(sensors);
return 0;
}

118
src/sys.c Normal file
View File

@@ -0,0 +1,118 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>
#include <time.h>
#include "log.h"
#include "sys.h"
#include "utils.h"
static sensor_file_t sensors;
char *sys_get_time_string(char *buf, const size_t bufsz, const time_t t) {
const struct tm *tm = localtime(&t);
snprintf(buf, bufsz, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
return buf;
}
char *sys_get_date_string(char *buf, const size_t bufsz, const time_t t) {
const struct tm *tm = localtime(&t);
constexpr char months[][4] = {"sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paz", "lis", "gru"};
constexpr char wdays[][4] = {"Nie", "Pon", "Wto", "Sro", "Czw", "Pia", "Sob"};
snprintf(buf, bufsz, "%s, %02d %s", wdays[tm->tm_wday], tm->tm_mday, months[tm->tm_mon]);
return buf;
}
void sys_get_load_avg(double *load_avg) {
getloadavg(load_avg, 3);
}
void sys_get_distro(char* name, char* ver_str) {
char buf[128];
FILE *f = fopen("/etc/os-release", "r");
if (f == nullptr)
return;
while (fgets(buf, sizeof(buf), f)) {
sscanf(buf, "NAME=\"%s\"", name); // NOLINT we live dangerously
sscanf(buf, "VERSION=\"%s\"", ver_str); // NOLINT (its not really that dangerous)
}
fclose(f);
}
void sys_get_ram(struct memory_t *ram) {
char buf[64];
memset(ram, 0, sizeof(struct memory_t)); // zero out all values so we dont break out of the loop early
FILE *f = fopen("/proc/meminfo", "r");
if (f == nullptr)
return;
while (fgets(buf, sizeof(buf), f)) {
sscanf(buf, "MemTotal: %lu kB", &ram->total); // NOLINT we live dangerously
sscanf(buf, "MemAvailable: %lu kB", &ram->free); // NOLINT (its not really that dangerous)
if (ram->total && ram->free)
break;
}
fclose(f);
ram->used = ram->total - ram->free;
}
void sys_get_disk(struct disk_t *disk) {
struct statvfs diskData;
statvfs(disk->mountpoint, &diskData);
disk->usage.free = diskData.f_bfree * diskData.f_frsize;
disk->usage.used = (diskData.f_blocks - diskData.f_bfree) * diskData.f_frsize;
disk->usage.total = diskData.f_blocks * diskData.f_frsize;
}
void sys_get_network(struct network_t *network) {
struct _net_t {
struct timespec ts;
uint64_t rx;
uint64_t tx;
};
static struct _net_t prev;
struct _net_t current = {};
clock_gettime(CLOCK_MONOTONIC, &current.ts);
fast_read_sensor_into_mem(&network->f_rx, &current.rx);
fast_read_sensor_into_mem(&network->f_tx, &current.tx);
const int64_t diff_us = timespec_diff_us(&current.ts, &prev.ts);
const uint64_t rx_delta = current.rx >= prev.rx ? current.rx - prev.rx : 0;
const uint64_t tx_delta = current.tx >= prev.tx ? current.tx - prev.tx : 0;
const double diff_s = (double)diff_us / 1000000.0;
network->download = (uint32_t)((double)rx_delta / diff_s);
network->upload = (uint32_t)((double)tx_delta / diff_s);
prev = current;
// still fucky?
}
void sys_netdev_detect(struct network_t *network) {
applog(LOG_INF, "Opening netdev %s", network->device);
static char path[128];
snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/", network->device);
open_sensor(&network->f_rx, path, "rx_bytes", nullptr, 1, SENSOR_U64);
open_sensor(&network->f_tx, path, "tx_bytes", nullptr, 1, SENSOR_U64);
}
void sys_detect(struct syssensors_t *s, const char *platform) {
snprintf(s->hwmon, sizeof(s->hwmon), "/sys/devices/platform/%s/hwmon", platform);
const int ret = get_hwmon_dir(s->hwmon, sizeof(s->hwmon));
if (ret != 0) {
s->hwmon[0] = '\0';
applog(LOG_WRN, "Can't open system hwmon. Kernel params added? Module loaded? See README");
return;
}
applog(LOG_INF, "Detected system sensors in %s", s->hwmon);
open_sensor(&sensors, s->hwmon, "/temp5_input", &s->t_sensor, 1000, SENSOR_I16);
}
void sys_update_sensors(const struct syssensors_t *s) {
if (!s->hwmon[0])
return;
fast_read_sensor(&sensors);
}

View File

@@ -1,87 +0,0 @@
#include <sys/statvfs.h>
#include <charconv>
#include <chrono>
#include <format>
#include <log.hpp>
#include <string>
#include <sys.hpp>
#include <utils.hpp>
int getVal(const std::string_view target, std::string_view content) {
int result = -1;
if (const std::size_t start = content.find(target); start != std::string::npos) {
content.remove_prefix(start + target.length());
const std::size_t end = content.find(" kB");
while (std::isspace(content.front()))
content.remove_prefix(1);
std::from_chars(content.data(), content.data() + end, result);
}
return result;
}
void sys::getTimeDate(TimeDate* td) {
const auto now = std::chrono::system_clock::now();
const auto local = std::chrono::current_zone()->to_local(now);
const std::chrono::year_month_day ymd{floor<std::chrono::days>(local)};
const std::chrono::weekday wd{floor<std::chrono::days>(local)};
td->time = std::format("{:%H:%M:%S}", floor<std::chrono::seconds>(local));
td->date = std::format("{}, {} {}", wdays[wd.c_encoding()], ymd.day(), months[static_cast<unsigned>(ymd.month()) - 1]);
}
void sys::getRam(Memory* mem) {
static std::array<char, 128> buf; // file is longer but we only need first 128 chars
utils::readFile("/proc/meminfo", buf);
mem->free = getVal("MemAvailable:", buf.data());
mem->total = getVal("MemTotal:", buf.data()); // shouldn't really update this on each loop
mem->used = mem->total - mem->free;
}
void sys::getDisk(Disk* disk) {
struct statvfs diskData{};
statvfs(disk->mountpoint.c_str(), &diskData);
disk->usage.free = diskData.f_bfree * diskData.f_frsize;
disk->usage.used = (diskData.f_blocks - diskData.f_bfree) * diskData.f_frsize;
disk->usage.total = diskData.f_blocks * diskData.f_frsize;
}
void sys::getNetwork(Network* net, const std::string& device) {
struct Network {
std::chrono::time_point<std::chrono::steady_clock> ts;
uint64_t rx = 0;
uint64_t tx = 0;
};
static std::array<char, 32> buf;
static Network prev;
Network current;
current.ts = std::chrono::steady_clock::now();
utils::fastReadNum("/sys/class/net/" + device + "/statistics/rx_bytes", buf, current.rx);
utils::fastReadNum("/sys/class/net/" + device + "/statistics/tx_bytes", buf, current.tx);
const double ts_diff = static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(current.ts - prev.ts).count()) / 1000000.0f;
net->download = static_cast<uint32_t>(static_cast<double>(current.rx - prev.rx) / ts_diff);
net->upload = static_cast<uint32_t>(static_cast<double>(current.tx - prev.tx) / ts_diff);
prev = current;
}
int sys::detect(sysSensors* s, const std::string& platform) {
// we dont really need getFirstDir since its a static folder, but its useful for try/catch
try {
s->hwmon = utils::getFirstDir("/sys/devices/platform/" + platform + "/hwmon");
} catch (...) {
applog(LOG::ERR, "Can't open \"/sys/devices/platform/{}/hwmon\". Kernel params added? Module loaded?", platform);
return -1;
}
applog(LOG::INF, "Found system sensors");
return 0;
}
void sys::updateSensors(sysSensors* s) {
if (s->hwmon.empty())
return;
static std::array<char, 8> buf;
utils::fastReadNum(s->hwmon + "/fan2_input", buf, s->fan_speed[0]);
utils::fastReadNum(s->hwmon + "/fan7_input", buf, s->fan_speed[1]);
utils::fastReadNum<1000>(s->hwmon + "/temp5_input", buf, s->t_sensor);
}

151
src/utils.c Normal file
View File

@@ -0,0 +1,151 @@
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "log.h"
#include "utils.h"
int get_instance_lock() {
const char *xdg_runtime_dir = getenv("XDG_RUNTIME_DIR");
char tmp_dir[256];
snprintf(tmp_dir, sizeof(tmp_dir), "%s/linuxSmartie.lock", xdg_runtime_dir);
const int file = open(tmp_dir, O_CREAT | O_RDWR | O_CLOEXEC, 0600);
if (file < 0) {
applog(LOG_ERR, "Couldn't create a lock file at %s", tmp_dir);
return -1;
}
if (flock(file, LOCK_EX | LOCK_NB) < 0) {
if (errno == EWOULDBLOCK)
applog(LOG_ERR, "another instance already running, this one will exit.");
else
applog(LOG_ERR, "flock failed: %s", strerror(errno));
close(file);
return -1;
}
return 0;
}
inline void timespec_add_ms(struct timespec *ts, const uint16_t ms) {
ts->tv_nsec += ms * 1000000L;
if (ts->tv_nsec >= 1000000000L) {
ts->tv_sec++;
ts->tv_nsec -= 1000000000L;
}
}
inline int64_t timespec_diff_us(const struct timespec *a, const struct timespec *b) {
const int64_t sec = a->tv_sec - b->tv_sec;
const int64_t nsec = a->tv_nsec - b->tv_nsec;
return sec * 1000000LL + nsec / 1000LL;
}
inline char *pretty_num(const double num, char *buf, const size_t bufsz) {
const uint8_t decimals = (num <= 10.0) ? 2 : (num <= 100.0) ? 1 : 0;
snprintf(buf, bufsz, "%.*f", decimals, num);
return buf;
}
inline char *pretty_bytes(const uint64_t bytes, int8_t start_suffix, char *buf, const size_t bufsz) {
constexpr char suffix_list[] = {'K', 'M', 'G', 'T', 'P', 'E', 'Z'};
start_suffix *= -1;
double bytes_d = (double)bytes;
while (bytes_d >= 1024.0) {
bytes_d /= 1024.0;
start_suffix++;
}
snprintf(buf, bufsz, "%s%c", P_NUM(bytes_d), suffix_list[MAX(0, start_suffix)]);
return buf;
}
/* path has to have at least 8 free chars at the end for glob */
int get_hwmon_dir(char *path, const size_t bufsz) {
glob_t g;
strcat(path, "/hwmon*");
if (glob(path, 0, nullptr, &g) == 0) {
strlcpy(path, g.gl_pathv[0], bufsz); // same size as hwmon length, careful
globfree(&g);
return 0;
}
return -1;
}
void open_sensor(sensor_file_t* s, const char* dir, const char *file, void* value, const uint32_t divider, const sensor_type_t type) {
char path[256];
char *nt = stpcpy(path, dir); // nt is a pointer to the null terminator of the copied string
strcpy(nt, file); // copy to the null terminator of prev copy, append basically
s->divider = divider;
s->value = value;
s->type = type;
s->fd = open(path, O_RDONLY | O_CLOEXEC);
if (s->fd < 0)
applog(LOG_ERR, "couldn't open %s", path);
}
inline void fast_read_sensor_into_mem(const sensor_file_t *s, void* val) {
if (s->fd < 0) {
applog(LOG_ERR, "invalid sensor fd %d", s->fd);
return;
}
char buf[32];
lseek(s->fd, 0, SEEK_SET);
const ssize_t len = read(s->fd, buf, sizeof(buf));
if (len <= 0) {
applog(LOG_ERR, "error reading sensor");
return;
}
buf[len] = '\0';
switch (s->type) {
case SENSOR_I16:
*(int16_t*)val = (int16_t)(strtol(buf, nullptr, 10) / s->divider);
break;
case SENSOR_U8:
*(uint8_t*)val = (uint8_t)(strtol(buf, nullptr, 10) / s->divider);
break;
case SENSOR_U16:
*(uint16_t*)val = (uint16_t)(strtol(buf, nullptr, 10) / s->divider);
break;
case SENSOR_U64:
*(uint64_t*)val = strtoull(buf, nullptr, 10) / s->divider;
break;
case SENSOR_FLOAT:
const uint64_t t = strtoull(buf, nullptr, 10);
*(float*)val = (float)t / (float)s->divider;
break;
default:
applog(LOG_ERR, "unknown sensor type %d", s->type);
break;
}
}
inline void fast_read_sensor(const sensor_file_t *s) {
if (s->value != nullptr) {
fast_read_sensor_into_mem(s, s->value);
return;
}
applog(LOG_ERR, "attempted to read into nullptr");
}
void close_sensor(const sensor_file_t* s) {
close(s->fd);
}

View File

@@ -1,32 +0,0 @@
#include <utils.hpp>
#include <cstdint>
#include <filesystem>
#include <string>
#include <string_view>
namespace utils {
/* pads given string to length */
std::string pad(std::string s, const uint8_t len, const just j) {
if (len > s.size())
s.insert(j == left ? s.end() : s.begin(), len - s.size(), fill_char);
return s;
}
/* returns the string centered within given length */
std::string center(std::string str, const uint8_t len) {
const uint8_t padding = len - str.size();
str.insert(str.begin(), padding / 2, fill_char);
str.insert(str.end(), padding / 2, fill_char);
return str;
}
/* finds first dir in path and returns it. don't refactor, you don't understand it, its fast enough, and you only use it on init */
std::string getFirstDir(const std::string_view& path) {
for (const auto& entry : std::filesystem::directory_iterator(path))
return entry.path();
return "";
}
} // namespace utils