Weekly rotation through items in Sitemap

I am currently working on a weakly cleaning plan.
The plan shall show the 5 main rooms with the name underneath.
The name is tied to a switch that is, at the end of the week, used to check if the task has been completed.
So far, so simple.
When a new week beginns, it shall move the switches by one step, so that each one has a new task for the coming week.

For example, on week 1 it should look like this:

            Text label="Livingroom"
			Switch item=Franz 	mappings=[ON="Done"] 	visibility=[Franz!=ON]
			Text label="Bath"
			Switch item=Tobi	mappings=[ON="Done"]	visibility=[Tobi!=ON]
			Text label="Balkony"
			Switch item=Till	mappings=[ON="Done"]	visibility=[Till!=ON]
			Text label="Kitchen"
			Switch item=Timm	mappings=[ON="Done"]	visibility=[Timm!=ON]
			Text label="Graveyard"
			Switch item=Jill	mappings=[ON="Done"]	visibility=[Jill!=ON]

and at the start of the next week, it should rotate:

            Text label="Livingroom"
			Switch item=Tobi	mappings=[ON="Done"]	visibility=[Tobi!=ON]
			Text label="Bath"
			Switch item=Till	mappings=[ON="Done"]	visibility=[Till!=ON]
			Text label="Balkony"
			Switch item=Timm	mappings=[ON="Done"]	visibility=[Timm!=ON]
			Text label="Kitchen"
			Switch item=Jill	mappings=[ON="Done"]	visibility=[Jill!=ON]
			Text label="Graveyard"
			Switch item=Franz 	mappings=[ON="Done"] 	visibility=[Franz!=ON]      

I have to admit, I’m at a complete loss with how to implement this rotation, since all i have seen so far indiacates, that the Sidemap cannot be changed that easily.

Thanks in advance

Think I would restructure the approach here.
If you had an Item representing “Jill’s job”, you can manage and display the person’s job for the week without changing sitemaps. The Item doesn’t change, only its content (state).
A separate Item can record Jill’s job (whatever it happens to be) as done/not yet.

A key part of managing all that in rules will be a look-up table of some kind, and a pointer for this week’s start job.

Thanks for the reply,

what sort of item would you suggest to represent the job?
I have to admitt, until now, I only did some basic things with Openhab, so my knowledge of what can do what leaves much to be desired.
The “Job-done” is realised with a simple switch that has it’s state checked at the end of the week.
Also the table and pointer thing is a bit beyond me, do you have any material, where I can read about that?
Or some sort of quick example?

I haven’t seen any examples of anything quite like this. On the other hand, it’s probably a good example of programming next steps beyond “hello world” and simple sums. It would take a while to build a system part by part.

I rather think I would base it around a MAP transform, of all things.
That’ll give us a lookup table number->task
1=Bath
2=Kitchen etc.
The Items representing each worker’s task can then be number types internally, but the number is easy to convert to text for display.
Why hold the tasks as numbers? Easy to handle in rules e.g. find the next task by adding one.
If the Items are themselves given numbered names, that will give us a way to handle item_X in a rule that substitutes 1,2, etc. for X
It’s sort of planning ahead for the necessary manipulations.

So maybe the first job would be to define Items like

Number user_1 "Tobi [MAP(tasklist.map:%s]"

which we can use with sitemap

Switch item=user_1 mappings=[99="Done"]

So if we assign say 2 to the Item, we get to see a display like
<icon> Tobi Kitchen [Done]
which can be jazzed up later with colours etc.

The 99 here is just some magic number that we can capture with a rule to do whatever we need to do to signal task complete.

Making sense so far?

1 Like

Sorry for the late reply, had little time over the last week.

Up until now, i haven’t woked with any sort of transformation, so it is all a bit hard to wrap my head around.
But I will look into the MAP-transformation and see if I can make it work.

Thank you very much for your help. =)

It’s going to be harder than first appears.
We’re stuck with an essentially immutable sitemap, so I think that dictates how we manage the moving parts to fit it.

I have thought about it a bit more.
Working backwards from a sitemap entry like

Switch item=user_1 mappings=[99="To Do !"]  visibility=[user_1 < 100]
Text item=user_1 visibility=[user_1 > 99]

It’s non-obvious, but my idea here is that user_1 is linked to an individual like Tobi by hardcoding the Item label.

Number user_1 "Tobi [MAP(tasklist.map:%s]" <boy> {autoupdate="false"}

(autoupdate false here allows us to send the Item commands without an automatic state update - we’ll manage that by rule instead)

We can assign the Item state numeric values which correspond to tasks e.g. 2 for Kitchen.
With sleight of hand, we can (by rules) add 100 to distinguish between to-do and completed.
To turn the numbers into human readable text. the MAP

0=No task
1=Bath
2=Kitchen
... etc.
101=Bath completed
102=Kitchen completed

So the sitemap should make more sense now, and display look something like

<icon> Tobi            Kitchen [To Do!]

and when the todo button is pressed, a rule acts on the 99 command to update the Item by adding 100 to indicate completion, and doanything else it needs to like dispense pocket money…
Display now

<icon> Tobi            Kitchen completed

You might actually want to add an undo button here, in case of error (or failed workplace inspection).

It’s going to take some clever rules to work behind the scenes to set up each week, and you have to decide what to do about tasks not done. If you wanted to queue those up it gets much more complicated.

But first design the target you’re trying to achieve, for which I’ve outlined only a suggestion for above.
There are always other approaches!

1 Like

Works like a charm!

But I ran into a problem with the rotation.
It should count upwards, until all 5 Tasks have been done, than go back to the first and start over.
This is my current solution:

rule "Check1"
when
//Button to manualy test the rotation
Item Forward received command OFF
then
//Check if task is NOT done
if ((user_1.state as DecimalType).intValue < 100)
{
    //Increase "You failed to work"-counter
	user_1_SP.postUpdate((user_1_SP.state as DecimalType).intValue + 1)
    //Adcance counter to next task
	user_1.postUpdate((user_1.state as DecimalType).intValue + 1)
    //If task >=6
	if ((user_1.state as DecimalType).intValue >= 6)
	{
        //Reset to 1
		user_1.postUpdate(1)
	}
	
}
//Check if task is done
else if ((user_1.state as DecimalType).intValue > 100)
{
    //Advance counter to next task
	user_1.postUpdate((user_1.state as DecimalType).intValue - 99)
    //If task >=6
	if ((user_1.state as DecimalType).intValue >= 6)
	{
        //Reset to 1
		user_1.postUpdate(1)
	}
}
end

Now it get’s random.
Sometimes, the task-counter resets back to 1, when it reaches 6 and sometimes, it wont. In this case, it stays at 6 and returns to 1 on the next iteration, but the damage is done and the scedule is disrupted.
Is there any reason, why this happens at random?

Yes.

user_1.postUpdate((user_1.state ...
if ((user_1.state ...

That’s doomed to failure.
postupdate is asynchronous; the instruction to update the Item is sent to openHAB event bus, where it can be used for rule triggers, binding actions, etc.
But the rule does not stop and wait for all that to happen.
And it takes a little time.
If you retrieve the Item state in the next rule line, you will almost certainly get the 'old" state still.
Items are not simple rule variables.

You don’t need to get the state this way, after all you know what you just sent as postUpdate.
The usual answer is to process as a variable in the rule, and do the update separately. Psuedo code -

var taskNum = myItem.state etc.

if (taskNum < 100)
     // do naughty boy actions
else
     // pay pocket money, then
    taskNum = taskNum - 100  // normalize

taskNum = taskNum + 1
if taskNum > 6)
   taskNum = 1

myItem.postUpdate(taskNum)

Once you’ve got all that working, I suppose the next step is put all your user Items in a Group. The single rule can then iterate through all Group members.

We’ve not mentioned yet, you’ll need to persist/restore these user Items, so the tasklist survives a system reboot.
I’d actually do that last, and first develop something to give you an initial setting when starting up with Items state NuLL.

1 Like

Well, that cetainly explains it.

I changed everything according to your instructions, now it looks like this (and works like a charm):

rule "Check"
when
    //Button to manualy test the rotation
    Item Forward received command OFF
then
//Loop through items of group
Putztruppe.members.forEach [item|

//Cast user-Index as int to variable
TaskNum = (item.state as DecimalType).intValue

if (TaskNum < 100) {
    //Here is a Problem!!!
}
else {
    //Revert to basic Numbers 
	TaskNum = TaskNum - 100
}
//Increase index
TaskNum = TaskNum + 1

if (TaskNum >= 6) {
	TaskNum = 1
}
//"Write" index to Item
item.postUpdate(TaskNum)
]
end

The “Problem-Part” now is, that I want to acces the…let’s call them “Bad-Boy-Points” (BBP) for the corresponding user.
I.e. if user_1 has not done his task, the Number user_1_SP should increase by 1, same for user_2/user_2_SP and so on…
I haven’t found an elegant way to get this to “work”, except for using 5 IF-cases, comparing the name of the current item.
I thought, maybe there is an easier way, to do this, when the two names are very similar to each other.

Sounds like you might be looking for the “associated Items” naming technique

1 Like

Just a tiny note since I’m here.

Don’t force variable to primitives unless absolutely required. It greatly increases the amount of time required to parse the .rules files (it could add minutes or more in some circumstances).

And in this case it’s pointless. The result of TaskNum - 100 is going to be a BigDecimal anyway. So just let OH handle the types.

TasNum = item.state as Number

Also, this doesn’t help you with Rules DSL rules, but for those using the OH 3 Rules Engine (aka. NGRE) there is access to Item metadata in the rules. So you could put the mapping between Items in the Item definition. See Design Pattern: Using Item Metadata as an Alternative to Several DPs. A good example of using it for this kind of mapping is in Design Pattern: Debounce.

1 Like

Thanks to both of you, I changed everything according to your hints.

I found, what i want to do with the Associated Items, but somehow can’t get it to work.

rule "Check"
when
Item Forward received command OFF
then
Putztruppe.members.forEach [item|

var TaskNum = item.state as Number
var Testtext = item.name+"_SP"

if (TaskNum < 100) {
	var nummer = Testtext.state as Number
	Testtext.postUpdate(nummer + 1)
}
else

//Everything's the same from here

But i get the Error:

Rule 'Check': 'state' is not a member of 'java.lang.String'

I fooled around with this for an hour now and couldn’t find any solution.
I also tried

rule "Check"
when
Item Forward received command OFF
then
Putztruppe.members.forEach [item|

var TaskNum = item.state as Number

if (TaskNum < 100) {
	(item.name+"_SP").postUpdate((item.name+"_SP").state as DecimalType).intValue + 1)
}
else....

and so on.

What am i doing wrong here?

Pay close attention to the wording:

With the associated Item’s name, one can get a reference to that Item by pulling it out of a Group’s members using the findFirst method. Or one can pull it out of the Item registry directly.

All you have is the name of the Item. You don’t have the actual Item. The name of the Item is a String and a String doesn’t have a state.

Scroll down to the Item Registry example in the Associated Design Pattern article and read it again. You need to pull the actual Item out of the Item Registry, or find it by name from a Group (see the findFirst example).

But, if all you are doing is updating or commanding an Item, You do only need the Item’s name. See “postUpdate” Action example in the Associated Items DP article.

1 Like

Don’t know, how i could have overlooked that.

It works perfectly now, thank you very much.