Skip to content

Commit 5326cfd

Browse files
committed
Implemented presence widget
1 parent 0329dcd commit 5326cfd

47 files changed

Lines changed: 2397 additions & 1421 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ This adapter is built with the help of `type-detector`. All possible devices cou
131131
Placeholder for the next version (at the beginning of the line):
132132
### **WORK IN PROGRESS**
133133
-->
134+
### **WORK IN PROGRESS**
135+
* (@GermanBluefox) Added presence widget
136+
134137
### 2.0.7 (2026-04-09)
135138
* (@GermanBluefox) Added new widgets
136139

Lines changed: 226 additions & 226 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

admin/index_m.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
/>
2323
<script type="text/javascript" onerror="setTimeout(function(){window.location.reload()}, 5000)" src="./lib/js/socket.io.js"></script>
2424
<title>Devices</title>
25-
<script type="module" crossorigin src="./assets/index-1HzRpVZL.js"></script>
25+
<script type="module" crossorigin src="./assets/index-BuIpbn4l.js"></script>
2626
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
2727
<link rel="modulepreload" crossorigin href="./assets/virtual_mf-REMOTE_ENTRY_ID_iobroker_devices__remoteEntry_js-D5_qLLYC.js">
2828
<link rel="stylesheet" crossorigin href="./assets/index-pCeIEsc7.css">

admin/tab.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
/>
2323
<script type="text/javascript" onerror="setTimeout(function(){window.location.reload()}, 5000)" src="./lib/js/socket.io.js"></script>
2424
<title>Devices</title>
25-
<script type="module" crossorigin src="./assets/index-1HzRpVZL.js"></script>
25+
<script type="module" crossorigin src="./assets/index-BuIpbn4l.js"></script>
2626
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
2727
<link rel="modulepreload" crossorigin href="./assets/virtual_mf-REMOTE_ENTRY_ID_iobroker_devices__remoteEntry_js-D5_qLLYC.js">
2828
<link rel="stylesheet" crossorigin href="./assets/index-pCeIEsc7.css">

package-lock.json

Lines changed: 1 addition & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/dm-widgets/src/types.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,16 @@ export interface WidgetInfo extends ItemInfo {
6969
};
7070
}
7171

72-
export type CustomWidgetType = 'clock' | 'weather' | 'iframe' | 'wind' | 'gauge' | 'universal' | 'plugin';
72+
export type CustomWidgetType =
73+
| 'clock'
74+
| 'weather'
75+
| 'iframe'
76+
| 'wind'
77+
| 'gauge'
78+
| 'universal'
79+
| 'plugin'
80+
| 'newline'
81+
| 'presence';
7382

7483
export interface CategoryInfo extends ItemInfo {
7584
type: 'category';

src-admin/package-lock.json

Lines changed: 120 additions & 126 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-admin/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
"@dnd-kit/utilities": "^3.2.2",
99
"@iobroker/adapter-react-v5": "^8.1.6",
1010
"@iobroker/dm-widgets": "file:../packages/dm-widgets",
11-
"@iobroker/json-config": "^8.3.4",
12-
"@iobroker/type-detector": "^5.0.11",
11+
"@iobroker/json-config": "^8.3.8",
12+
"@iobroker/type-detector": "^5.0.13",
1313
"@iobroker/types": "^7.1.2-alpha.0-20260409-feacb179d",
1414
"@module-federation/runtime": "^2.3.0",
1515
"@module-federation/vite": "^1.13.5",

src-admin/src/WidgetsManager/Category.tsx

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
ViewModule,
2828
Star,
2929
StarBorder,
30+
Close,
3031
Warning,
3132
WaterDamage,
3233
WaterDrop,
@@ -89,8 +90,10 @@ import {
8990
WidgetGauge,
9091
WidgetUniversal,
9192
type WidgetUniversalSettings,
93+
WidgetPresence,
94+
type WidgetPresenceSettings,
9295
WidgetGate,
93-
PluginWidget,
96+
WidgetPlugin,
9497
type WidgetWeatherSettings,
9598
type WidgetIframeSettings,
9699
type WidgetWindSettings,
@@ -111,7 +114,7 @@ import type {
111114
DevicesDetectorState,
112115
CustomWidgetPlugin,
113116
} from '@iobroker/dm-widgets';
114-
import type { WidgetGeneric } from './Widgets/Generic';
117+
import { WidgetGeneric } from './Widgets/Generic';
115118

116119
interface CategoryProps {
117120
category: CategoryInfo;
@@ -223,10 +226,14 @@ type OrderedItem = {
223226
data: CategoryInfo | WidgetInfo | CustomWidgetBase;
224227
};
225228

226-
function getGridColumn(item: OrderedItem, widgetSettings: Record<string, WidgetSettingsBase>): string | undefined {
229+
function getGridColumn(item: OrderedItem, widgetSettings: Record<string, WidgetSettingsBase>, editing?: boolean): string | undefined {
227230
if (item.type === 'category') {
228231
return '1 / -1';
229232
}
233+
if (item.type === 'custom' && (item.data as CustomWidgetBase).type === 'newline') {
234+
// In edit mode: normal 1x1 grid item (draggable); play mode: full-row span for line break
235+
return editing ? undefined : '1 / -1';
236+
}
230237
let size: '1x1' | '2x0.5' | '2x1';
231238
if (item.type === 'widget') {
232239
size = widgetSettings[item.id]?.size || '1x1';
@@ -482,6 +489,7 @@ function SortableGrid(props: {
482489

483490
const orderedItems = liveOrder.map(id => itemMap.get(id)).filter(Boolean) as OrderedItem[];
484491
const activeItem = activeId ? itemMap.get(activeId) : null;
492+
const draggingNewline = activeItem?.type === 'custom' && (activeItem.data as CustomWidgetBase).type === 'newline';
485493

486494
const renderContent = (item: OrderedItem, isDropTarget?: boolean): React.ReactNode => {
487495
if (item.type === 'category') {
@@ -536,7 +544,7 @@ function SortableGrid(props: {
536544
}
537545

538546
const parentCatId = category.props.category.parent ? String(category.props.category.parent) : null;
539-
const showParentDrop = !!activeId && !!onMoveWidgetToCategory && !!parentCatId;
547+
const showParentDrop = !!activeId && !draggingNewline && !!onMoveWidgetToCategory && !!parentCatId;
540548

541549
return (
542550
<DndContext
@@ -587,14 +595,15 @@ function SortableGrid(props: {
587595
>
588596
{orderedItems.map(item => {
589597
const isCw = item.type === 'custom';
598+
const isNewline = isCw && (item.data as CustomWidgetBase).type === 'newline';
590599
const fav =
591-
item.type === 'category'
600+
item.type === 'category' || isNewline
592601
? undefined
593602
: isCw
594603
? (item.data as CustomWidgetBase).favorite
595604
: widgetSettings[item.id]?.favorite;
596605
const toggleFav =
597-
item.type === 'category'
606+
item.type === 'category' || isNewline
598607
? undefined
599608
: isCw
600609
? category.props.onToggleCustomWidgetFavorite
@@ -603,7 +612,7 @@ function SortableGrid(props: {
603612
<SortableItem
604613
key={item.id}
605614
id={item.id}
606-
gridColumn={getGridColumn(item, widgetSettings)}
615+
gridColumn={getGridColumn(item, widgetSettings, true)}
607616
isDragging={item.id === activeId}
608617
favorite={fav}
609618
onToggleFavorite={toggleFav}
@@ -686,14 +695,15 @@ function GroupSortableGrid(props: {
686695
>
687696
{items.map(item => {
688697
const isCw = item.type === 'custom';
698+
const isNewline = isCw && (item.data as CustomWidgetBase).type === 'newline';
689699
const fav =
690-
item.type === 'category'
700+
item.type === 'category' || isNewline
691701
? undefined
692702
: isCw
693703
? (item.data as CustomWidgetBase).favorite
694704
: widgetSettings[item.id]?.favorite;
695705
const toggleFav =
696-
item.type === 'category'
706+
item.type === 'category' || isNewline
697707
? undefined
698708
: isCw
699709
? category.props.onToggleCustomWidgetFavorite
@@ -702,7 +712,7 @@ function GroupSortableGrid(props: {
702712
<SortableItem
703713
key={item.id}
704714
id={item.id}
705-
gridColumn={getGridColumn(item, widgetSettings)}
715+
gridColumn={getGridColumn(item, widgetSettings, true)}
706716
isDragging={item.id === activeId}
707717
favorite={fav}
708718
onToggleFavorite={toggleFav}
@@ -1979,9 +1989,20 @@ export default class Category extends Component<CategoryProps, CategoryState> {
19791989
);
19801990
break;
19811991
}
1992+
case 'presence':
1993+
content = (
1994+
<WidgetPresence
1995+
key={def.id}
1996+
settings={def as WidgetPresenceSettings}
1997+
stateContext={this.props.stateContext}
1998+
onOpenSettings={settingsCb}
1999+
onRemove={removeCb}
2000+
/>
2001+
);
2002+
break;
19822003
case 'plugin':
19832004
content = (
1984-
<PluginWidget
2005+
<WidgetPlugin
19852006
key={def.id}
19862007
id={def.id}
19872008
stateContext={this.props.stateContext}
@@ -1994,6 +2015,60 @@ export default class Category extends Component<CategoryProps, CategoryState> {
19942015
/>
19952016
);
19962017
break;
2018+
case 'newline':
2019+
content = removeCb ? (
2020+
<Box
2021+
key={def.id}
2022+
sx={theme => WidgetGeneric.getStyleCompact(theme)}
2023+
>
2024+
<Box
2025+
sx={theme => ({
2026+
display: 'flex',
2027+
flexDirection: 'column',
2028+
alignItems: 'center',
2029+
justifyContent: 'center',
2030+
boxSizing: 'border-box',
2031+
width: '100%',
2032+
aspectRatio: '1',
2033+
border: '2px dashed',
2034+
borderColor:
2035+
theme.palette.mode === 'dark'
2036+
? 'rgba(255,255,255,0.2)'
2037+
: 'rgba(0,0,0,0.15)',
2038+
borderRadius: 'inherit',
2039+
gap: 0.5,
2040+
})}
2041+
>
2042+
<Typography sx={{ fontSize: 'max(36px, 20cqi)', lineHeight: 1, opacity: 0.4 }}>
2043+
2044+
</Typography>
2045+
<Typography
2046+
variant="caption"
2047+
sx={{ opacity: 0.3, userSelect: 'none', fontSize: 'max(0.6rem, 3cqi)' }}
2048+
>
2049+
{I18n.t('wm_New line')}
2050+
</Typography>
2051+
</Box>
2052+
<IconButton
2053+
size="small"
2054+
onClick={() => removeCb()}
2055+
sx={{
2056+
position: 'absolute',
2057+
top: 6,
2058+
right: 6,
2059+
p: '3px',
2060+
opacity: 0,
2061+
'&:hover': { opacity: 1 },
2062+
'.MuiBox-root:hover > &': { opacity: 0.7 },
2063+
}}
2064+
>
2065+
<Close sx={{ fontSize: 16 }} />
2066+
</IconButton>
2067+
</Box>
2068+
) : (
2069+
<Box key={def.id} />
2070+
);
2071+
break;
19972072
default:
19982073
content = null;
19992074
}

0 commit comments

Comments
 (0)